Moved application code to the Application layer

Imported Win10 project
Code cleanup
WIP - Use common UWP services for Win8.1 and Win10
This commit is contained in:
Geoffroy BONNEVILLE
2020-04-06 20:20:45 +02:00
parent e795a8c3c4
commit 56d93a5187
292 changed files with 48614 additions and 837 deletions

View File

@@ -0,0 +1,206 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Application.Common.Interfaces;
using ModernKeePass.Common;
using ModernKeePass.Domain.Interfaces;
namespace ModernKeePass.Views.BasePages
{
public class LayoutAwarePageBase: Page
{
/// <summary>
/// NavigationHelper is used on each page to aid in navigation and
/// process lifetime management
/// </summary>
public NavigationHelper NavigationHelper { get; }
public ListView ListView { get; set; }
public virtual CollectionViewSource ListViewSource { get; set; }
public virtual IHasSelectableObject Model { get; set; }
public LayoutAwarePageBase()
{
// Setup the navigation helper
NavigationHelper = new NavigationHelper(this);
NavigationHelper.LoadState += navigationHelper_LoadState;
NavigationHelper.SaveState += navigationHelper_SaveState;
// Setup the logical page navigation components that allow
// the page to only show one pane at a time.
NavigationHelper.GoBackCommand = new RelayCommand(GoBack, CanGoBack);
// Start listening for Window size changes
// to change from showing two panes to showing a single pane
Window.Current.SizeChanged += Window_SizeChanged;
InvalidateVisualState();
}
protected void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Invalidate the view state when logical page navigation is in effect, as a change
// in selection may cause a corresponding change in the current logical page. When
// an item is selected this has the effect of changing from displaying the item list
// to showing the selected item's details. When the selection is cleared this has the
// opposite effect.
if (!UsingLogicalPageNavigation()) return;
NavigationHelper.GoBackCommand.RaiseCanExecuteChanged();
InvalidateVisualState();
}
/// <summary>
/// Populates the page with content passed during navigation. Any saved state is also
/// provided when recreating a page from a prior session.
/// </summary>
/// <param name="sender">
/// The source of the event; typically <see cref="Common.NavigationHelper"/>
/// </param>
/// <param name="e">Event data that provides both the navigation parameter passed to
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested and
/// a dictionary of state preserved by this page during an earlier
/// session. The state will be null the first time a page is visited.</param>
protected void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
if (e.PageState == null)
{
// When this is a new page, select the first item automatically unless logical page
// navigation is being used (see the logical page navigation #region below.)
if (!UsingLogicalPageNavigation())
{
ListViewSource.View?.MoveCurrentToFirst();
}
}
else
{
// Restore the previously saved state associated with this page
if (e.PageState.ContainsKey("SelectedItem"))
{
ListViewSource.View?.MoveCurrentTo(e.PageState["SelectedItem"]);
}
}
}
/// <summary>
/// Preserves state associated with this page in case the application is suspended or the
/// page is discarded from the navigation cache. Values must conform to the serialization
/// requirements of <see cref="Common.SuspensionManager.SessionState"/>.
/// </summary>
/// <param name="sender">The source of the event; typically <see cref="Common.NavigationHelper"/></param>
/// <param name="e">Event data that provides an empty dictionary to be populated with
/// serializable state.</param>
protected void navigationHelper_SaveState(object sender, SaveStateEventArgs e)
{
if (ListViewSource.View != null)
{
e.PageState["SelectedItem"] = Model?.SelectedItem;
}
}
#region Logical page navigation
// The split page is designed so that when the Window does have enough space to show
// both the list and the details, only one pane will be shown at at time.
//
// This is all implemented with a single physical page that can represent two logical
// pages. The code below achieves this goal without making the user aware of the
// distinction.
protected const int MinimumWidthForSupportingTwoPanes = 768;
/// <summary>
/// Invoked to determine whether the page should act as one logical page or two.
/// </summary>
/// <returns>True if the window should show act as one logical page, false
/// otherwise.</returns>
protected bool UsingLogicalPageNavigation()
{
return Window.Current.Bounds.Width < MinimumWidthForSupportingTwoPanes;
}
/// <summary>
/// Invoked with the Window changes size
/// </summary>
/// <param name="sender">The current Window</param>
/// <param name="e">Event data that describes the new size of the Window</param>
protected void Window_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
InvalidateVisualState();
}
protected bool CanGoBack()
{
if (UsingLogicalPageNavigation() && ListView.SelectedItem != null)
{
return true;
}
return NavigationHelper.CanGoBack();
}
protected void GoBack()
{
if (UsingLogicalPageNavigation() && ListView.SelectedItem != null)
{
// When logical page navigation is in effect and there's a selected item that
// item's details are currently displayed. Clearing the selection will return to
// the item list. From the user's point of view this is a logical backward
// navigation.
ListView.SelectedItem = null;
}
else
{
NavigationHelper.GoBack();
}
}
protected void InvalidateVisualState()
{
var visualState = DetermineVisualState();
VisualStateManager.GoToState(this, visualState, false);
NavigationHelper.GoBackCommand.RaiseCanExecuteChanged();
}
/// <summary>
/// Invoked to determine the name of the visual state that corresponds to an application
/// view state.
/// </summary>
/// <returns>The name of the desired visual state. This is the same as the name of the
/// view state except when there is a selected item in portrait and snapped views where
/// this additional logical page is represented by adding a suffix of _Detail.</returns>
protected string DetermineVisualState()
{
if (!UsingLogicalPageNavigation())
return "PrimaryView";
// Update the back button's enabled state when the view state changes
var logicalPageBack = UsingLogicalPageNavigation() && ListView?.SelectedItem != null;
return logicalPageBack ? "SinglePane_Detail" : "SinglePane";
}
#endregion
#region NavigationHelper registration
/// The methods provided in this section are simply used to allow
/// NavigationHelper to respond to the page's navigation methods.
///
/// Page specific logic should be placed in event handlers for the
/// <see cref="Common.NavigationHelper.LoadState"/>
/// and <see cref="Common.NavigationHelper.SaveState"/>.
/// The navigation parameter is available in the LoadState method
/// in addition to page state preserved during an earlier session.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
NavigationHelper.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
NavigationHelper.OnNavigatedFrom(e);
}
#endregion
}
}

View File

@@ -0,0 +1,266 @@
<Page
x:Class="ModernKeePass.Views.EntriesPage"
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:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:actions="using:ModernKeePass.Actions"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
xmlns:userControls="using:ModernKeePass.Views.UserControls"
xmlns:listItems="using:ModernKeePass.ViewModels.ListItems"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<converters:IconToSymbolConverter x:Key="IconToSymbolConverter"/>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter" />
<converters:DoubleToSolidColorBrushConverter x:Key="DoubleToForegroundBrushComplexityConverter" />
</Page.Resources>
<controls:MasterDetailsView
x:Name="MasterDetailList"
Style="{StaticResource ReorderMasterDetailsView}"
ItemsSource="{x:Bind ViewModel.Entries, Mode=OneWay}"
CompactModeThresholdWidth="720"
SelectedItem="{x:Bind ViewModel.SelectedEntry, Mode=OneWay}">
<controls:MasterDetailsView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Padding" Value="0" />
</Style>
</controls:MasterDetailsView.ItemContainerStyle>
<controls:MasterDetailsView.ItemTemplate>
<DataTemplate x:DataType="listItems:EntryItemViewModel">
<Grid x:Name="EntryGrid" Padding="12,0,12,0" Background="{Binding BackgroundColor, ConverterParameter={StaticResource SystemControlPageBackgroundTransparentBrush}, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<SymbolIcon Grid.Column="0" Symbol="{x:Bind Icon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=0}" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource SystemControlPageTextBaseMediumBrush}, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}" />
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="15">
<TextBlock Text="{x:Bind Name, Mode=OneWay}" TextWrapping="NoWrap" FontWeight="SemiBold" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource SystemControlPageTextBaseMediumBrush}, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}" />
<TextBlock Text="{x:Bind UserName, Mode=OneWay}" MaxHeight="60" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource SystemControlPageTextBaseMediumBrush}, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}" />
<TextBlock Text="{x:Bind Url, Mode=OneWay}" MaxHeight="60" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource SystemControlPageTextBaseMediumBrush}, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}"/>
</StackPanel>
<Grid.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="EntryItemCopyLogin" Icon="Page2">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{x:Bind UserName, Mode=OneWay}" />
<actions:ToastAction x:Uid="ToastCopyLogin" Title="{x:Bind Name, Mode=OneWay}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemCopyPassword" Icon=" ProtectedDocument">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{x:Bind Password, Mode=OneWay}" />
<actions:ToastAction x:Uid="ToastCopyPassword" Title="{x:Bind Name, Mode=OneWay}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemCopyUrl" Icon="OpenFile" IsEnabled="{x:Bind HasUrl, Mode=OneWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:NavigateToUrlAction Url="{x:Bind Url, Mode=OneWay}" />
<actions:ToastAction x:Uid="ToastCopyUrl" Title="{x:Bind Name, Mode=OneWay}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutSeparator />
<MenuFlyoutItem x:Uid="TopMenuDeleteFlyout" Icon="Delete" Click="DeleteFlyoutItem_OnClick" />
</MenuFlyout>
</Grid.ContextFlyout>
</Grid>
</DataTemplate>
</controls:MasterDetailsView.ItemTemplate>
<controls:MasterDetailsView.MasterCommandBar>
<CommandBar
DefaultLabelPosition="Right"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
HorizontalAlignment="Stretch"
FontFamily="Segoe UI">
<AppBarToggleButton x:Name="AddEntryButton" x:Uid="AppBarAddEntry" Icon="Add">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked">
<!--<core:ChangePropertyAction TargetObject="{Binding ElementName=AddEntryButton}" PropertyName="IsEnabled" Value="False" />-->
<core:ChangePropertyAction TargetObject="{Binding ElementName=NewEntryNameTextBox}" PropertyName="Visibility" Value="Visible" />
<actions:SetupFocusAction TargetObject="{Binding ElementName=NewEntryNameTextBox}" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<!--<core:ChangePropertyAction TargetObject="{Binding ElementName=AddEntryButton}" PropertyName="IsEnabled" Value="False" />-->
<core:ChangePropertyAction TargetObject="{Binding ElementName=NewEntryNameTextBox}" PropertyName="Visibility" Value="Collapsed" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarToggleButton>
</CommandBar>
</controls:MasterDetailsView.MasterCommandBar>
<controls:MasterDetailsView.MasterHeader>
<TextBox x:Name="NewEntryNameTextBox" Visibility="Collapsed" KeyUp="NewEntryNameTextBox_OnKeyDown" />
</controls:MasterDetailsView.MasterHeader>
<controls:MasterDetailsView.DetailsTemplate>
<DataTemplate x:DataType="listItems:EntryItemViewModel">
<!--<Frame x:Name="ContentFrame" SourcePageType="pages:EntryPage" />-->
<Pivot>
<Pivot.TitleTemplate>
<DataTemplate x:DataType="listItems:EntryItemViewModel">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Style="{ThemeResource TitleTextBlockStyle}" />
</StackPanel>
</DataTemplate>
</Pivot.TitleTemplate>
<PivotItem x:Uid="EntryPivotItemMain" Margin="0">
<Grid Background="White">
<ScrollViewer>
<StackPanel Margin="20">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="0,20,0,0"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Margin" Value="0,20,0,0"/>
<Setter Property="FontSize" Value="18"/>
</Style>
<Style TargetType="TextBox" x:Key="EntryTextBoxWithButtonStyle">
<Setter Property="Width" Value="350"/>
<Setter Property="Height" Value="32"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
</StackPanel.Resources>
<TextBlock x:Uid="EntryLogin" />
<TextBox Text="{x:Bind UserName, Mode=TwoWay}" Style="{StaticResource EntryTextBoxWithButtonStyle}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{x:Bind UserName}" />
<actions:ToastAction x:Uid="ToastCopyLogin" Title="{x:Bind Name}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<TextBlock x:Uid="EntryPassword" />
<PasswordBox x:Name="Password"
HorizontalAlignment="Left"
Password="{x:Bind Password, Mode=TwoWay}"
Width="350"
Height="32"
PasswordRevealMode="Hidden">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{x:Bind Password}" />
<actions:ToastAction x:Uid="ToastCopyPassword" Title="{x:Bind Name}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</PasswordBox>
<ProgressBar
Maximum="128"
Width="350"
HorizontalAlignment="Left"
Value="{x:Bind PasswordComplexityIndicator, ConverterParameter=0\,128, Converter={StaticResource ProgressBarLegalValuesConverter}}"
Foreground="{x:Bind PasswordComplexityIndicator, ConverterParameter=128, Converter={StaticResource DoubleToForegroundBrushComplexityConverter}}" />
<CheckBox x:Uid="EntryShowPassword" HorizontalAlignment="Left" Margin="0">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked">
<core:ChangePropertyAction TargetObject="{Binding ElementName=Password}" PropertyName="PasswordRevealMode" Value="Visible" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<core:ChangePropertyAction TargetObject="{Binding ElementName=Password}" PropertyName="PasswordRevealMode" Value="Hidden" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</CheckBox>
<TextBlock TextWrapping="Wrap" Text="URL" FontSize="18"/>
<TextBox Text="{x:Bind Url, Mode=TwoWay}" MaxLength="256" Style="{StaticResource EntryTextBoxWithButtonStyle}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:NavigateToUrlAction Url="{x:Bind Url}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<TextBlock x:Uid="EntryNotes" />
<TextBox
HorizontalAlignment="Left"
TextWrapping="Wrap"
Text="{x:Bind Notes, Mode=TwoWay}"
Width="350"
Height="200"
AcceptsReturn="True"
IsSpellCheckEnabled="True" />
<CheckBox
x:Uid="EntryExpirationDate"
IsChecked="{x:Bind HasExpirationDate, Mode=TwoWay}" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<SymbolIcon
Grid.Column="0"
Symbol="Important"
Foreground="DarkRed"
Visibility="{x:Bind HasExpired}">
<ToolTipService.ToolTip>
<ToolTip x:Uid="EntryExpirationTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<StackPanel Grid.Column="1" x:Name="ExpirationDatePanel" Visibility="{x:Bind HasExpirationDate, Mode=OneWay}">
<DatePicker Date="{x:Bind ExpiryDate, Mode=TwoWay}" Style="{StaticResource MainColorDatePicker}" />
<TimePicker Margin="0,10,0,0" Time="{x:Bind ExpiryTime, Mode=TwoWay}" Style="{StaticResource MainColorTimePicker}" />
</StackPanel>
</Grid>
</StackPanel>
</ScrollViewer>
</Grid>
</PivotItem>
<PivotItem x:Uid="EntryPivotItemAdditional">
<ItemsControl ItemsSource="{x:Bind AdditionalFields}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Key}" />
<TextBox Text="{Binding Value, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</PivotItem>
<PivotItem x:Uid="EntryPivotItemHistory">
<ListView ItemsSource="{x:Bind History}" SelectionChanged="HistoryListView_SelectionChanged" />
</PivotItem>
<PivotItem x:Uid="EntryPivotItemSettings">
<ScrollViewer>
<StackPanel Orientation="Vertical">
<TextBlock x:Uid="EntryIcon" />
<userControls:SymbolPickerUserControl
SelectedSymbol="{Binding IconId, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=0, Mode=TwoWay}" />
<TextBlock x:Uid="EntryBackgroundColor" Margin="0,0,10,0" />
<ColorPicker
HorizontalAlignment="Left"
ColorSpectrumShape="Ring"
IsColorPreviewVisible="False"
IsColorChannelTextInputVisible="False"
IsHexInputVisible="False"
LostFocus="ColorPickerBackground_LostFocus" />
<TextBlock x:Uid="EntryForegroundColor" Margin="0,0,10,0" />
<ColorPicker
HorizontalAlignment="Left"
ColorSpectrumShape="Ring"
IsColorPreviewVisible="False"
IsColorChannelTextInputVisible="False"
IsHexInputVisible="False"
LostFocus="ColorPickerForeground_LostFocus" />
</StackPanel>
</ScrollViewer>
</PivotItem>
</Pivot>
</DataTemplate>
</controls:MasterDetailsView.DetailsTemplate>
</controls:MasterDetailsView>
</Page>

View File

@@ -0,0 +1,94 @@
using System;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
using Autofac;
using ModernKeePass.Domain.Interfaces;
using ModernKeePass.ViewModels;
using ModernKeePass.ViewModels.ListItems;
// TODO: check https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlListView/cs/Samples/MasterDetailSelection
namespace ModernKeePass.Views
{
public partial class EntriesPage
{
private readonly IDatabaseService _databaseService;
private readonly IResourceService _resourceService;
public EntriesViewModel ViewModel { get; set; }
public EntriesPage(): this(App.Container.Resolve<IDatabaseService>(), App.Container.Resolve<IResourceService>())
{ }
public EntriesPage(IDatabaseService databaseService, IResourceService resourceService)
{
_databaseService = databaseService;
_resourceService = resourceService;
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is GroupItemViewModel group) ViewModel = new EntriesViewModel(group);
}
private async void DeleteFlyoutItem_OnClick(object sender, RoutedEventArgs e)
{
if (sender is MenuFlyoutItem flyout)
{
var item = (EntryItemViewModel)flyout.DataContext;
var deleteFileDialog = new ContentDialog
{
Title = $"{_resourceService.GetResourceValue("EntityDeleteActionButton")} {item.Name} ?",
Content = _databaseService.IsRecycleBinEnabled
? _resourceService.GetResourceValue("EntryRecyclingConfirmation")
: _resourceService.GetResourceValue("EntryDeletingConfirmation"),
PrimaryButtonText = _resourceService.GetResourceValue("EntityDeleteActionButton"),
CloseButtonText = _resourceService.GetResourceValue("EntityDeleteCancelButton")
};
var result = await deleteFileDialog.ShowAsync();
// Delete the file if the user clicked the primary button.
// Otherwise, do nothing.
// TODO: move this logic to the service
if (result == ContentDialogResult.Primary)
{
ViewModel.Entries.Remove(item);
}
}
}
private void NewEntryNameTextBox_OnKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Enter)
{
e.Handled = true;
var text = NewEntryNameTextBox.Text;
if (string.IsNullOrEmpty(text)) return;
ViewModel.AddNewEntry(text);
AddEntryButton.IsChecked = false;
}
else if (e.Key == VirtualKey.Escape)
{
AddEntryButton.IsChecked = false;
}
}
private void ColorPickerBackground_LostFocus(object sender, RoutedEventArgs e)
{
if (sender is ColorPicker colorPicker) ((EntryItemViewModel) colorPicker.DataContext).BackgroundColor = colorPicker.Color;
}
private void ColorPickerForeground_LostFocus(object sender, RoutedEventArgs e)
{
if (sender is ColorPicker colorPicker) ((EntryItemViewModel) colorPicker.DataContext).ForegroundColor = colorPicker.Color;
}
private void HistoryListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
}
}

View File

@@ -0,0 +1,168 @@
<Page
x:Class="ModernKeePass.Views.EntryPage"
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:userControls="using:ModernKeePass.Views.UserControls"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:ModernKeePass.Actions"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:listItems="using:ModernKeePass.ViewModels.ListItems"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter" />
<converters:DoubleToSolidColorBrushConverter x:Key="DoubleToForegroungBrushComplexityConverter" />
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter" />
<converters:IconToSymbolConverter x:Key="IntToSymbolConverter" />
</Page.Resources>
<Grid>
<Pivot>
<Pivot.TitleTemplate>
<DataTemplate x:DataType="listItems:EntryItemViewModel">
<Grid>
<TextBlock Text="{x:Bind Name}" Style="{ThemeResource HeaderTextBlockStyle}" />
</Grid>
</DataTemplate>
</Pivot.TitleTemplate>
<PivotItem Header="Main" Margin="0">
<Grid Background="White">
<RelativePanel>
<StackPanel Margin="20">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="0,20,0,0"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Margin" Value="0,20,0,0"/>
<Setter Property="FontSize" Value="18"/>
</Style>
<Style TargetType="TextBox" x:Key="EntryTextBoxWithButtonStyle">
<Setter Property="Width" Value="350"/>
<Setter Property="Height" Value="32"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
</StackPanel.Resources>
<TextBlock x:Uid="EntryLogin" />
<TextBox
Text="{x:Bind ViewModel.UserName, Mode=TwoWay}"
Style="{StaticResource EntryTextBoxWithButtonStyle}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{x:Bind ViewModel.UserName}" />
<actions:ToastAction x:Uid="ToastCopyLogin" Title="{x:Bind ViewModel.Name}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<TextBlock x:Uid="EntryPassword" />
<PasswordBox x:Name="Password"
HorizontalAlignment="Left"
Password="{x:Bind ViewModel.Password, Mode=TwoWay}"
Width="350"
Height="32"
PasswordRevealMode="Hidden">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{x:Bind ViewModel.Password}" />
<actions:ToastAction x:Uid="ToastCopyPassword" Title="{x:Bind ViewModel.Name}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</PasswordBox>
<ProgressBar
Maximum="128"
Width="350"
HorizontalAlignment="Left"
Value="{x:Bind ViewModel.PasswordComplexityIndicator, ConverterParameter=0\,128, Converter={StaticResource ProgressBarLegalValuesConverter}, Mode=OneWay}"
Foreground="{x:Bind ViewModel.PasswordComplexityIndicator, ConverterParameter=128, Converter={StaticResource DoubleToForegroungBrushComplexityConverter}, Mode=OneWay}" />
<CheckBox
x:Uid="EntryShowPassword"
HorizontalAlignment="Left"
Margin="0">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked">
<core:ChangePropertyAction TargetObject="{Binding ElementName=Password}" PropertyName="PasswordRevealMode" Value="Visible" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<core:ChangePropertyAction TargetObject="{Binding ElementName=Password}" PropertyName="PasswordRevealMode" Value="Hidden" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</CheckBox>
<TextBlock TextWrapping="Wrap" Text="URL" FontSize="18"/>
<TextBox
Text="{x:Bind ViewModel.Url, Mode=TwoWay}"
MaxLength="256"
Style="{StaticResource EntryTextBoxWithButtonStyle}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:NavigateToUrlAction Url="{x:Bind ViewModel.Url}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<TextBlock x:Uid="EntryNotes" />
<TextBox
HorizontalAlignment="Left"
TextWrapping="Wrap"
Text="{x:Bind ViewModel.Notes, Mode=TwoWay}"
Width="350"
Height="200"
AcceptsReturn="True"
IsSpellCheckEnabled="True" />
<CheckBox
x:Uid="EntryExpirationDate"
IsChecked="{x:Bind ViewModel.HasExpirationDate, Mode=TwoWay}" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<SymbolIcon
Grid.Column="0"
Symbol="Important"
Foreground="DarkRed"
Visibility="{x:Bind ViewModel.HasExpired}">
<ToolTipService.ToolTip>
<ToolTip x:Uid="EntryExpirationTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<StackPanel
Grid.Column="1"
x:Name="ExpirationDatePanel"
Visibility="{x:Bind ViewModel.HasExpirationDate, Mode=OneWay}">
<DatePicker
Date="{x:Bind ViewModel.ExpiryDate, Mode=TwoWay}"
Style="{StaticResource MainColorDatePicker}" />
<TimePicker
Margin="0,10,0,0"
Time="{x:Bind ViewModel.ExpiryTime, Mode=TwoWay}"
Style="{StaticResource MainColorTimePicker}" />
</StackPanel>
</Grid>
</StackPanel>
</RelativePanel>
</Grid>
</PivotItem>
<PivotItem Header="Additional">
<StackPanel Orientation="Vertical">
<TextBlock x:Uid="EntryIcon" />
<userControls:SymbolPickerUserControl
SelectedSymbol="{x:Bind ViewModel.Icon, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=0, Mode=TwoWay}" />
<TextBlock x:Uid="EntryBackgroundColor" />
<userControls:ColorPickerUserControl
HorizontalAlignment="Left"
SelectedColor="{x:Bind ViewModel.BackgroundColor, Converter={StaticResource ColorToBrushConverter}, Mode=TwoWay}" />
<TextBlock x:Uid="EntryForegroundColor" />
<userControls:ColorPickerUserControl
SelectedColor="{x:Bind ViewModel.ForegroundColor, Converter={StaticResource ColorToBrushConverter}, Mode=TwoWay}" />
</StackPanel>
</PivotItem>
<PivotItem Header="History" />
</Pivot>
</Grid>
</Page>

View File

@@ -0,0 +1,21 @@
using Windows.UI.Xaml.Navigation;
using ModernKeePass.ViewModels.ListItems;
namespace ModernKeePass.Views
{
public partial class EntryPage
{
public EntryItemViewModel ViewModel { get; set; }
public EntryPage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter is EntryItemViewModel entry) ViewModel = entry;
}
}
}

View File

@@ -0,0 +1,249 @@
<Page
x:Class="ModernKeePass.Views.GroupsPage"
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:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:actions="using:ModernKeePass.Actions"
xmlns:listItems="using:ModernKeePass.ViewModels.ListItems"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<converters:IconToSymbolConverter x:Key="IconToSymbolConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
</Page.Resources>
<!-- SplitView -->
<SplitView x:Name="SplitView"
CompactPaneLength="{StaticResource MenuSize}"
OpenPaneLength="320"
IsPaneOpen="True"
FontFamily="Segoe UI">
<SplitView.Pane>
<Grid x:Name="SplitViewPane">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid
x:Name="TopButtonsBorder"
Grid.Row="0"
Grid.Column="0"
BorderThickness="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
IsEnabled="{x:Bind Frame.CanGoBack}"
Margin="0"
BorderThickness="0"
Style="{StaticResource NavigationBackButtonNormalStyle}"
Click="BackButton_OnClick" />
<TextBox x:Name="NewGroupNameTextBox"
Grid.Column="1"
Visibility="Visible"
HorizontalAlignment="Right"
Text="{x:Bind ViewModel.NewGroupName, Mode=TwoWay}"
Height="30"
Width="230"
KeyUp="NewGroupNameTextBox_KeyDown"
LostFocus="NewGroupNameTextBox_LostFocus" />
<Button x:Name="AddButton"
Grid.Column="2"
Margin="0"
Width="{StaticResource MenuSize}"
Height="{StaticResource MenuSize}"
Background="Transparent"
VerticalAlignment="Top"
BorderThickness="0"
FontFamily="Segoe MDL2 Assets"
Content="&#xE710;">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:ChangePropertyAction TargetObject="{Binding ElementName=NewGroupNameTextBox}" PropertyName="Visibility" Value="Visible"/>
<core:ChangePropertyAction TargetObject="{Binding ElementName=AddButton}" PropertyName="IsEnabled" Value="False"/>
<actions:SetupFocusAction TargetObject="{Binding ElementName=NewEntryNameTextBox}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Button>
</Grid>
<Grid
Grid.Row="1"
Grid.Column="0"
BorderThickness="0">
<AutoSuggestBox
x:Name="AutoSuggestBox"
x:Uid="GroupSearch"
Width="250"
HorizontalAlignment="Center"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
QueryIcon="Find"
TextMemberPath="Name"
TextChanged="AutoSuggestBox_TextChanged"
QuerySubmitted="AutoSuggestBox_QuerySubmitted">
<AutoSuggestBox.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Padding" Value="0" />
</Style>
</AutoSuggestBox.ItemContainerStyle>
<AutoSuggestBox.ItemTemplate>
<DataTemplate x:DataType="listItems:EntryItemViewModel">
<Grid x:Name="EntryGrid" Padding="12,0,12,0" Background="{Binding BackgroundColor, ConverterParameter={StaticResource SystemControlPageBackgroundTransparentBrush}, Converter={StaticResource ColorToBrushConverter}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<SymbolIcon Grid.Column="0" Symbol="{x:Bind Icon, Converter={StaticResource IconToSymbolConverter}, ConverterParameter=0}" Foreground="{StaticResource SystemControlPageTextBaseHighBrush}" />
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="15">
<TextBlock Text="{Binding Name}" TextWrapping="NoWrap" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource SystemControlPageTextBaseHighBrush}, Converter={StaticResource ColorToBrushConverter}}"/>
<TextBlock Text="{x:Bind Parent.Text}" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource SystemControlPageTextBaseMediumBrush}, Converter={StaticResource ColorToBrushConverter}}" MaxHeight="60" />
</StackPanel>
</Grid>
</DataTemplate>
</AutoSuggestBox.ItemTemplate>
</AutoSuggestBox>
</Grid>
<!-- Navigation Tree -->
<TreeView x:Name="NavigationTree"
Grid.Row="2"
ItemsSource="{x:Bind ViewModel.RootItemViewModel.Children}"
ItemInvoked="NavigationTree_OnItemInvoked">
<TreeView.ItemTemplate>
<DataTemplate x:DataType="listItems:GroupItemViewModel">
<TreeViewItem ItemsSource="{x:Bind Children}">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Visibility="{x:Bind IsEditMode, Mode=OneWay, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
<SymbolIcon Symbol="{x:Bind Symbol, Converter={StaticResource IconToSymbolConverter}}" />
<TextBlock x:Name="GroupName" Text="{x:Bind Text, Mode=OneWay}" Width="200" Margin="10,0,0,0" />
<StackPanel.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="TopMenuDeleteFlyout" Icon="Delete" Click="DeleteFlyoutItem_OnClick" />
<MenuFlyoutItem x:Uid="TopMenuRenameFlyout" Click="RenameFlyoutItem_OnClick" />
</MenuFlyout>
</StackPanel.ContextFlyout>
</StackPanel>
<TextBox
Text="{x:Bind Text, Mode=TwoWay}"
Visibility="{x:Bind IsEditMode, Mode=OneWay}"
Width="200"
KeyDown="GroupNameTextBox_KeyDown"
LostFocus="GroupNameTextBox_OnLostFocus" />
</Grid>
</TreeViewItem>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<CommandBar DefaultLabelPosition="Right"
Grid.Row="3"
Background="Transparent"
HorizontalAlignment="Center"
FontFamily="Segoe UI">
<AppBarButton x:Uid="AppBarHome" Icon="Home">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.MainPage10" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton x:Uid="AppBarSettings" Icon="Setting">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.SettingsPage10" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
</CommandBar>
</Grid>
</SplitView.Pane>
<SplitView.Content>
<StackPanel Orientation="Vertical">
<Button Margin="0"
Height="{StaticResource MenuSize}"
Background="Transparent"
BorderThickness="0"
Click="HamburgerButton_OnClick">
<StackPanel Orientation="Horizontal" Margin="5,0,0,0">
<FontIcon x:Name="HamburgerButton" FontFamily="Segoe MDL2 Assets" Glyph="&#xE700;" />
<TextBlock Text="{x:Bind ViewModel.Title, Mode=OneWay}" FontFamily="Segoe UI" FontWeight="Bold" Margin="20,0,0,0" VerticalAlignment="Center" />
</StackPanel>
</Button>
<!-- Navigation Frame -->
<Frame x:Name="SplitViewFrame"
Padding="0"
FontFamily="Segoe UI">
<Frame.ContentTransitions>
<TransitionCollection>
<NavigationThemeTransition>
<NavigationThemeTransition.DefaultNavigationTransitionInfo>
<DrillInNavigationTransitionInfo />
</NavigationThemeTransition.DefaultNavigationTransitionInfo>
</NavigationThemeTransition>
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
</StackPanel>
</SplitView.Content>
<!-- Responsive Visual States -->
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<!-- VisualState to be triggered when window width is >=1007 effective pixels -->
<VisualState x:Name="Expanded">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="HamburgerButton.Visibility" Value="Collapsed" />
<Setter Target="SplitView.DisplayMode" Value="Inline" />
<Setter Target="SplitViewPane.Background" Value="{ThemeResource SystemControlChromeHighAcrylicWindowMediumBrush}" />
<Setter Target="TopButtonsBorder.Background" Value="{ThemeResource SystemControlChromeHighAcrylicWindowMediumBrush}" />
</VisualState.Setters>
</VisualState>
<!-- VisualState to be triggered when window width is >=640 and <=1007 effective pixels -->
<!-- Skipped -->
<!--<VisualState x:Name="Compact">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="641" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="SplitView.DisplayMode"
Value="CompactOverlay" />
<Setter Target="SplitView.IsPaneOpen"
Value="False" />
</VisualState.Setters>
</VisualState>-->
<!-- VisualState to be triggered when window width is >=0 and <641 effective pixels -->
<VisualState x:Name="Minimal">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="HamburgerButton.Visibility" Value="Visible" />
<Setter Target="SplitView.DisplayMode" Value="CompactInline" />
<Setter Target="SplitViewPane.Background" Value="{ThemeResource SystemControlChromeHighAcrylicElementMediumBrush}" />
<Setter Target="TopButtonsBorder.Background" Value="Transparent" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</SplitView>
</Page>

View File

@@ -0,0 +1,194 @@
using System;
using System.Linq;
using Windows.ApplicationModel.Core;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Navigation;
using Autofac;
using ModernKeePass.Domain.Entities;
using ModernKeePass.Domain.Interfaces;
using ModernKeePass.ViewModels.ListItems;
using ModernKeePass.ViewModels;
namespace ModernKeePass.Views
{
public partial class GroupsPage
{
private readonly IDatabaseService _databaseService;
private readonly IResourceService _resourceService;
public GroupsViewModel ViewModel { get; set; }
public GroupsPage(): this(App.Container.Resolve<IDatabaseService>(), App.Container.Resolve<IResourceService>())
{ }
public GroupsPage(IDatabaseService databaseService, IResourceService resourceService)
{
InitializeComponent();
SetTitleBar();
_databaseService = databaseService;
_resourceService = resourceService;
}
private void SetTitleBar()
{
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
//coreTitleBar.LayoutMetricsChanged += CoreTitleBar_LayoutMetricsChanged;
//coreTitleBar.IsVisibleChanged += CoreTitleBar_IsVisibleChanged;
coreTitleBar.ExtendViewIntoTitleBar = false;
//UpdateTitleBarLayout(coreTitleBar);
// Set XAML element as a draggable region.
//Window.Current.SetTitleBar(AppTitleBar);
}
/*private void CoreTitleBar_IsVisibleChanged(CoreApplicationViewTitleBar sender, object args) =>
AppTitleBar.Visibility = sender.IsVisible ? Visibility.Visible : Visibility.Collapsed;
private void CoreTitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar coreTitleBar, object args) => UpdateTitleBarLayout(coreTitleBar);
private void UpdateTitleBarLayout(CoreApplicationViewTitleBar coreTitleBar)
{
// Get the size of the caption controls area and back button
// (returned in logical pixels), and move your content around as necessary.
LeftPaddingColumn.Width = new GridLength(coreTitleBar.SystemOverlayLeftInset);
RightPaddingColumn.Width = new GridLength(coreTitleBar.SystemOverlayRightInset);
//BackBorder.Margin = new Thickness(0, 0, coreTitleBar.SystemOverlayLeftInset, 0);
AutoSuggestBox.Margin = new Thickness(0, 5, coreTitleBar.SystemOverlayRightInset, 5);
// Update title bar control size as needed to account for system size changes.
//AppTitleBar.Height = coreTitleBar.Height;
AppTitleBar.Height = 44;
}*/
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!(e.Parameter is GroupEntity group)) group = _databaseService.RootGroupEntity;
ViewModel = new GroupsViewModel(group);
}
private void HamburgerButton_OnClick(object sender, RoutedEventArgs e)
{
//SplitView.IsPaneOpen = !SplitView.IsPaneOpen;
switch (SplitView.DisplayMode)
{
case SplitViewDisplayMode.Inline:
SplitView.DisplayMode = SplitViewDisplayMode.CompactInline;
break;
case SplitViewDisplayMode.CompactInline:
SplitView.DisplayMode = SplitViewDisplayMode.Inline;
break;
}
}
private void BackButton_OnClick(object sender, RoutedEventArgs e)
{
if (Frame.CanGoBack)
{
Frame.GoBack();
}
}
private void NavigationTree_OnItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args)
{
if (args.InvokedItem is GroupItemViewModel group)
{
ViewModel.Title = group.Text;
SplitViewFrame.Navigate(typeof(EntriesPage), group);
}
}
private void RenameFlyoutItem_OnClick(object sender, RoutedEventArgs e)
{
if (sender is MenuFlyoutItem flyout) ((GroupItemViewModel)flyout.DataContext).IsEditMode = true;
}
private async void DeleteFlyoutItem_OnClick(object sender, RoutedEventArgs e)
{
if (sender is MenuFlyoutItem flyout)
{
var item = (GroupItemViewModel) flyout.DataContext;
var deleteFileDialog = new ContentDialog
{
Title = $"{_resourceService.GetResourceValue("EntityDeleteActionButton")} {item.Text} ?",
Content = _databaseService.IsRecycleBinEnabled
? _resourceService.GetResourceValue("GroupRecyclingConfirmation")
: _resourceService.GetResourceValue("GroupDeletingConfirmation"),
PrimaryButtonText = _resourceService.GetResourceValue("EntityDeleteActionButton"),
CloseButtonText = _resourceService.GetResourceValue("EntityDeleteCancelButton")
};
var result = await deleteFileDialog.ShowAsync();
// Delete the file if the user clicked the primary button.
// Otherwise, do nothing.
// TODO: move this logic to the service
if (result == ContentDialogResult.Primary)
{
item.ParentViewModel.Children.Remove(item);
// TODO: refresh treeview
}
}
}
private void GroupNameTextBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Enter) GroupNameTextBox_OnLostFocus(sender, null);
}
private void GroupNameTextBox_OnLostFocus(object sender, RoutedEventArgs e)
{
if (sender is TextBox textBox) ((GroupItemViewModel)textBox.DataContext).IsEditMode = false;
}
private void NewGroupNameTextBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Enter)
{
e.Handled = true;
var text = ((TextBox)sender).Text;
if (string.IsNullOrEmpty(text)) return;
ViewModel.AddNewGroup(text);
AddButton.IsEnabled = true;
NewGroupNameTextBox.Visibility = Visibility.Collapsed;
NewGroupNameTextBox.Text = string.Empty;
}
}
private void AutoSuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
// Only get results when it was a user typing,
// otherwise assume the value got filled in by TextMemberPath
// or the handler for SuggestionChosen.
if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
{
//Set the ItemsSource to be your filtered dataset
//sender.ItemsSource = dataset;
sender.ItemsSource = ViewModel.RootItemViewModel.SubEntries.Where(e => e.Name.IndexOf(sender.Text, StringComparison.OrdinalIgnoreCase) >= 0);
}
}
private void AutoSuggestBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
if (args.ChosenSuggestion != null)
{
// User selected an item from the suggestion list, take an action on it here.
}
else
{
// Use args.QueryText to determine what to do.
}
}
private void NewGroupNameTextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(NewGroupNameTextBox.Text))
{
NewGroupNameTextBox.Visibility = Visibility.Collapsed;
}
}
}
}

View File

@@ -0,0 +1,36 @@
<Page
x:Class="ModernKeePass.Views.MainPage10"
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:controls="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid x:Name="AppTitleBar" Background="Transparent" />
<controls:NavigationView
x:Name="NavigationView"
IsBackButtonVisible="Collapsed"
Header="ModernKeePass"
PaneTitle="ModernKeePass"
ItemInvoked="NavigationView_OnItemInvoked"
Loaded="NavigationView_OnLoaded">
<controls:NavigationView.MenuItems>
<controls:NavigationViewItem x:Name="Welcome" Tag="welcome" Visibility="Collapsed" />
<controls:NavigationViewItem x:Uid="MainMenuItemOpen" x:Name="Open" Icon="Page2" Tag="open" />
<controls:NavigationViewItem x:Uid="MainMenuItemNew" x:Name="New" Icon="Add" Tag="new" />
<controls:NavigationViewItem x:Uid="MainMenuItemSave" x:Name="Save" Icon="Save" IsEnabled="{x:Bind ViewModel.IsDatabaseOpened, Mode=OneTime}" Tag="save" />
<controls:NavigationViewItem x:Uid="MainMenuItemRecent" x:Name="Recent" Icon="Copy" Tag="recent" />
<controls:NavigationViewItem x:Uid="MainMenuItemAbout" x:Name="About" Icon="Help" Tag="about" />
<controls:NavigationViewItem x:Uid="MainMenuItemDonate" x:Name="Donate" Icon="Shop" Tag="donate" />
<controls:NavigationViewItemSeparator/>
<controls:NavigationViewItemHeader x:Uid="MainPageOpenedDatabasesHeader" Visibility="{x:Bind ViewModel.IsDatabaseOpened, Mode=OneWay}"/>
<controls:NavigationViewItem x:Name="Database" Content="{x:Bind ViewModel.OpenedDatabaseName, Mode=OneWay}" Icon="ProtectedDocument" Visibility="{x:Bind ViewModel.IsDatabaseOpened, Mode=OneWay}" Tag="database"/>
</controls:NavigationView.MenuItems>
<Frame x:Name="ContentFrame" Margin="24"/>
</controls:NavigationView>
</Grid>
</Page>

View File

@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.ApplicationModel.Core;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Navigation;
using MUXC = Microsoft.UI.Xaml.Controls;
using ModernKeePass.ViewModels;
namespace ModernKeePass.Views
{
public partial class MainPage10
{
private MainViewModel ViewModel { get; }
private readonly IList<(string Tag, Type Page)> _pages = new List<(string Tag, Type Page)>
{
("welcome", typeof(WelcomePage)),
("open", typeof(OpenDatabasePage)),
("new", typeof(NewDatabasePage)),
("save", typeof(SaveDatabasePage)),
("recent", typeof(RecentDatabasesPage)),
("about", typeof(AboutPage)),
("donate", typeof(DonatePage)),
("database", typeof(GroupsPage))
};
public MainPage10()
{
InitializeComponent();
ViewModel = new MainViewModel();
SetTitleBar();
}
private void SetTitleBar()
{
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
coreTitleBar.ExtendViewIntoTitleBar = true;
Window.Current.SetTitleBar(AppTitleBar);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
ViewModel.File = e.Parameter as StorageFile;
}
private void NavigationView_OnItemInvoked(MUXC.NavigationView navigationView, MUXC.NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked)
Frame.Navigate(typeof(SettingsPage10));
else
{
// Getting the Tag from Content (args.InvokedItem is the content of NavigationViewItem)
var navItem = NavigationView.MenuItems
.OfType<MUXC.NavigationViewItem>()
.First(i => args.InvokedItem.Equals(i.Content));
NavigationView_Navigate(navItem);
}
}
private void NavigationView_Navigate(MUXC.NavigationViewItem navItem, object parameter = null)
{
var (_, page) = _pages.First(p => p.Tag.Equals(navItem.Tag));
NavigationView.Header = navItem.Content;
ContentFrame.Navigate(page, parameter);
}
private void NavigationView_OnLoaded(object sender, RoutedEventArgs e)
{
object parameter = null;
if (ViewModel.IsDatabaseOpened)
{
NavigationView.SelectedItem = Save;
parameter = Frame;
}
else if (ViewModel.File != null)
{
NavigationView.SelectedItem = Open;
parameter = ViewModel.File;
}
else if (ViewModel.HasRecentItems)
{
NavigationView.SelectedItem = Recent;
}
else
{
NavigationView.SelectedItem = Welcome;
}
NavigationView_Navigate((MUXC.NavigationViewItem)NavigationView.SelectedItem, parameter);
}
}
}

View File

@@ -0,0 +1,39 @@
<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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ModernKeePass.ViewModels"
x:Class="ModernKeePass.Views.AboutPage"
mc:Ignorable="d">
<Page.DataContext>
<viewModels:AboutViewModel/>
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="10,0,0,0">
<Run Text="{Binding Name}"/>
<Run Text="version"/>
<Run Text="{Binding Version}" />
</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
<Run x:Uid="AboutDesc" />
</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
<Run x:Uid="AboutHomepage" />
<Hyperlink NavigateUri="https://wismna.github.io/ModernKeePass/" Foreground="{StaticResource MainColor}">
<Run Text="https://wismna.github.io/ModernKeePass/"/>
</Hyperlink>
</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="10,0,0,0">
<Run x:Uid="AboutCreditsLabel" />
</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
<Run x:Uid="AboutCredits1" />
</TextBlock>
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="30,0,0,0">
<Run x:Uid="AboutCredits2" />
</TextBlock>
</StackPanel>
</Page>

View File

@@ -0,0 +1,15 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class AboutPage
{
public AboutPage()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,20 @@
<Page
x:Class="ModernKeePass.Views.DonatePage"
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:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
mc:Ignorable="d">
<Grid>
<ProgressRing x:Name="LoadingRing" IsActive="True" Width="50" Height="50" Foreground="{StaticResource MainColor}" />
<WebView Source="https://PayPal.Me/wismna">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="DOMContentLoaded">
<core:ChangePropertyAction TargetObject="{Binding ElementName=LoadingRing}" PropertyName="IsActive" Value="False" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</WebView>
</Grid>
</Page>

View File

@@ -0,0 +1,15 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class DonatePage
{
public DonatePage()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,37 @@
<Page
x:Class="ModernKeePass.Views.ImportExportPage"
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"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="200" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Import" Style="{StaticResource SubheaderTextBlockStyle}" />
<HyperlinkButton Grid.Column="0" Grid.Row="1" Content="Select file..." Style="{StaticResource MainColorHyperlinkButton}" Click="ImportFileButton_OnClick" />
<StackPanel Grid.Column="1" Grid.Row="1" >
<TextBlock Text="Format" Style="{StaticResource BodyTextBlockStyle}" Margin="0,0,0,10" />
<ComboBox Style="{StaticResource MainColorComboBox}">
<ComboBoxItem>CSV</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Grid.Column="2" Grid.Row="1" >
<TextBlock Text="Import into..." Style="{StaticResource BodyTextBlockStyle}" Margin="0,0,0,10" />
<RadioButton GroupName="ImportDestination" Content="New database" />
<RadioButton GroupName="ImportDestination" Content="Currently opened database" />
</StackPanel>
<Button Grid.Column="3" Grid.Row="1" Content="Import" Style="{StaticResource MainColorButton}" />
</Grid>
</Page>

View File

@@ -0,0 +1,35 @@
using System;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views
{
/// <summary>
/// The import/export page.
/// </summary>
public sealed partial class ImportExportPage
{
public ImportExportPage()
{
InitializeComponent();
}
private async void ImportFileButton_OnClick(object sender, RoutedEventArgs e)
{
var picker =
new FileOpenPicker
{
ViewMode = PickerViewMode.List,
SuggestedStartLocation = PickerLocationId.DocumentsLibrary
};
picker.FileTypeFilter.Add(".csv");
// Application now has read/write access to the picked file
var file = await picker.PickSingleFileAsync();
if (file == null) return;
}
}
}

View File

@@ -0,0 +1,47 @@
<Page
x:Class="ModernKeePass.Views.NewDatabasePage"
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:userControls="using:ModernKeePass.Views.UserControls"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<HyperlinkButton x:Uid="NewCreateButton" Click="CreateDatabaseButton_OnClick" Foreground="{StaticResource MainColor}" Style="{StaticResource MainColorHyperlinkButton}" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" x:Uid="NewCreateDesc" />
<Border HorizontalAlignment="Left" BorderThickness="1" BorderBrush="AliceBlue" Width="550" Visibility="{x:Bind ViewModel.IsFileSelected, Mode=OneWay}">
<StackPanel Margin="25,0,25,0">
<TextBlock Text="{x:Bind ViewModel.Name}" />
<userControls:UpdateCredentialsUserControl x:Uid="CompositeKeyNewButton" DatabaseFilePath="{x:Bind ViewModel.DatabaseFilePath, Mode=OneWay}" CredentialsUpdated="CompositeKeyUserControl_OnValidationChecked" />
</StackPanel>
</Border>
<CheckBox x:Name="CheckBox" x:Uid="NewImportCheckbox" Margin="15,10,0,0" IsChecked="{x:Bind ViewModel.IsImportChecked, Mode=TwoWay}" Visibility="{x:Bind ViewModel.IsFileSelected}" />
<Border HorizontalAlignment="Left" BorderThickness="1" BorderBrush="AliceBlue" Width="550" Visibility="{Binding IsChecked, ElementName=CheckBox}">
<StackPanel Margin="25,0,25,0">
<StackPanel Orientation="Horizontal">
<TextBlock x:Uid="NewImportFormat" Margin="0,15,0,10" Style="{StaticResource BodyTextBlockStyle}" />
<ComboBox Style="{StaticResource MainColorComboBox}" Margin="15,15,0,0" SelectionChanged="ImportFormatComboBox_OnSelectionChanged">
<ComboBoxItem>CSV</ComboBoxItem>
</ComboBox>
<Button Margin="5,10,0,0" Style="{StaticResource TextBlockButtonStyle}">
<SymbolIcon Symbol="Help" RenderTransformOrigin="0.5,0.5" >
<SymbolIcon.RenderTransform>
<CompositeTransform ScaleX="0.7" ScaleY="0.7"/>
</SymbolIcon.RenderTransform>
</SymbolIcon>
<Button.Flyout>
<Flyout>
<TextBlock Text="{x:Bind ViewModel.ImportFormatHelp}" TextWrapping="WrapWholeWords" MaxWidth="400" />
</Flyout>
</Button.Flyout>
</Button>
</StackPanel>
<HyperlinkButton x:Name="ImportFileLink" x:Uid="NewImportFile" Margin="-15,0,0,0" Style="{StaticResource MainColorHyperlinkButton}" Click="ImportFileButton_OnClick" />
</StackPanel>
</Border>
</StackPanel>
</Page>

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using Windows.Storage.AccessCache;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Autofac;
using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Enums;
using ModernKeePass.Domain.Interfaces;
using ModernKeePass.Events;
using ModernKeePass.ViewModels;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class NewDatabasePage
{
private Frame _mainFrame;
private readonly IResourceService _resourceService;
public NewViewModel ViewModel { get; }
public NewDatabasePage(): this(App.Container.Resolve<IResourceService>())
{ }
public NewDatabasePage(IResourceService resourceService)
{
InitializeComponent();
ViewModel = new NewViewModel();
_resourceService = resourceService;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
_mainFrame = e.Parameter as Frame;
}
private async void CreateDatabaseButton_OnClick(object sender, RoutedEventArgs e)
{
var savePicker = new FileSavePicker
{
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
SuggestedFileName = "New Database"
};
savePicker.FileTypeChoices.Add("KeePass 2.x database", new List<string> { ".kdbx" });
var file = await savePicker.PickSaveFileAsync();
if (file == null) return;
var token = StorageApplicationPermissions.FutureAccessList.Add(file);
var fileInfo = new FileInfo
{
Path = token,
Name = file.DisplayName
};
await ViewModel.OpenFile(fileInfo);
}
private void ImportFormatComboBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = sender as ComboBox;
switch (comboBox?.SelectedIndex)
{
case 0:
ViewModel.ImportFormat = ImportFormat.CSV;
ViewModel.ImportFileExtensionFilter = ".csv";
ViewModel.ImportFormatHelp = _resourceService.GetResourceValue("NewImportFormatHelpCSV");
break;
}
}
private async void ImportFileButton_OnClick(object sender, RoutedEventArgs e)
{
var picker =
new FileOpenPicker
{
ViewMode = PickerViewMode.List,
SuggestedStartLocation = PickerLocationId.DocumentsLibrary
};
if (!string.IsNullOrEmpty(ViewModel.ImportFileExtensionFilter))
picker.FileTypeFilter.Add(ViewModel.ImportFileExtensionFilter);
// Application now has read/write access to the picked file
ViewModel.ImportFile = await picker.PickSingleFileAsync();
if (ViewModel.ImportFile != null) ImportFileLink.Content = ViewModel.ImportFile.Name;
}
private void CompositeKeyUserControl_OnValidationChecked(object sender, EventArgs e)
{
ViewModel.PopulateInitialData();
_mainFrame.Navigate(typeof(GroupsPage));
}
}
}

View File

@@ -0,0 +1,30 @@
<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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:userControls="using:ModernKeePass.Views.UserControls"
x:Class="ModernKeePass.Views.OpenDatabasePage"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<HyperlinkButton x:Uid="OpenBrowseButton" Click="ButtonBase_OnClick" Foreground="{StaticResource MainColor}" Style="{StaticResource MainColorHyperlinkButton}" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" x:Uid="OpenBrowseDesc" />
<!--<HyperlinkButton x:Uid="OpenUrlButton" IsEnabled="False" Foreground="{StaticResource MainColor}" Style="{StaticResource MainColorHyperlinkButton}" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" x:Uid="OpenUrlDesc" />-->
<Border HorizontalAlignment="Left" BorderThickness="1" BorderBrush="AliceBlue" Width="550" Visibility="{x:Bind ViewModel.IsFileSelected, Mode=OneWay}">
<StackPanel Margin="25,0,25,0">
<TextBlock Text="{x:Bind ViewModel.Name, Mode=OneWay}" />
<userControls:CredentialsUserControl x:Uid="CompositeKeyOpenButton" DatabaseFilePath="{x:Bind ViewModel.DatabaseFilePath, Mode=OneWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ValidationChecked">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.GroupsPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</userControls:CredentialsUserControl>
</StackPanel>
</Border>
</StackPanel>
</Page>

View File

@@ -0,0 +1,58 @@
using System;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.ViewModels;
using Windows.Storage.AccessCache;
using ModernKeePass.Domain.Dtos;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class OpenDatabasePage
{
public OpenViewModel ViewModel { get; }
public OpenDatabasePage()
{
InitializeComponent();
ViewModel = new OpenViewModel();
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.Parameter is FileInfo fileInfo)
{
await ViewModel.OpenFile(fileInfo);
}
}
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var picker =
new FileOpenPicker
{
ViewMode = PickerViewMode.List,
SuggestedStartLocation = PickerLocationId.DocumentsLibrary
};
picker.FileTypeFilter.Add(".kdbx");
// Application now has read/write access to the picked file
var file = await picker.PickSingleFileAsync();
if (file == null) return;
var token = StorageApplicationPermissions.FutureAccessList.Add(file);
var fileInfo = new FileInfo
{
Path = token,
Name = file.DisplayName
};
await ViewModel.OpenFile(fileInfo);
}
}
}

View File

@@ -0,0 +1,70 @@
<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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:userControls="using:ModernKeePass.Views.UserControls"
xmlns:listItems="using:ModernKeePass.ViewModels.ListItems"
x:Class="ModernKeePass.Views.RecentDatabasesPage"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource x:Name="RecentItemsSource" Source="{x:Bind ViewModel.RecentItems}" />
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<HyperlinkButton Grid.Row="0"
HorizontalAlignment="Right"
Style="{StaticResource MainColorHyperlinkButton}"
Foreground="{StaticResource MainColor}"
Command="{x:Bind ViewModel.ClearAllCommand}">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Cancel" />
<TextBlock x:Uid="RecentClear" VerticalAlignment="Center" Margin="10,0,0,0" />
</StackPanel>
</HyperlinkButton>
<ListView Grid.Row="1"
ItemsSource="{Binding Source={StaticResource RecentItemsSource}}"
SelectedItem="{x:Bind ViewModel.SelectedItem, Mode=TwoWay}"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<ListView.DataContext>
<listItems:RecentItemViewModel />
</ListView.DataContext>
<ListView.ItemTemplate>
<DataTemplate x:DataType="listItems:RecentItemViewModel">
<Grid Margin="10,0,10,0">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Text="{x:Bind Name}" Padding="5,0,0,0" />
<!--<TextBlock Grid.Row="1" Text="{Binding Path}" Padding="5,0,0,0" FontSize="10" />-->
<userControls:CredentialsUserControl Grid.Row="2"
x:Name="DatabaseUserControl"
x:Uid="CompositeKeyOpenButton"
HorizontalAlignment="Stretch"
MinWidth="400"
Margin="0,10,0,0"
Visibility="{x:Bind IsSelected}"
DatabaseFilePath="{x:Bind Token}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ValidationChecked">
<!--<core:CallMethodAction TargetObject="{Binding}" MethodName="UpdateAccessTime" />-->
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.GroupsPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</userControls:CredentialsUserControl>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>

View File

@@ -0,0 +1,20 @@
// Pour en savoir plus sur le modèle d'élément Page vierge, consultez la page http://go.microsoft.com/fwlink/?LinkId=234238
using ModernKeePass.ViewModels;
namespace ModernKeePass.Views
{
/// <summary>
/// Une page vide peut être utilisée seule ou constituer une page de destination au sein d'un frame.
/// </summary>
public sealed partial class RecentDatabasesPage
{
private RecentViewModel ViewModel { get; }
public RecentDatabasesPage()
{
InitializeComponent();
ViewModel = new RecentViewModel();
}
}
}

View File

@@ -0,0 +1,15 @@
<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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="ModernKeePass.Views.SaveDatabasePage"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<HyperlinkButton x:Uid="SaveButton" Click="SaveButton_OnClick" Foreground="{StaticResource MainColor}" Style="{StaticResource MainColorHyperlinkButton}" />
<TextBlock x:Uid="SaveDesc" Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" />
<HyperlinkButton x:Uid="SaveAsButton" Click="SaveAsButton_OnClick" Foreground="{StaticResource MainColor}" Style="{StaticResource MainColorHyperlinkButton}" />
<TextBlock x:Uid="SaveAsDesc" Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" />
</StackPanel>
</Page>

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using Windows.Storage.AccessCache;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.ViewModels;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class SaveDatabasePage
{
private Frame _mainFrame;
public SaveViewModel ViewModel { get; }
public SaveDatabasePage()
{
InitializeComponent();
ViewModel = new SaveViewModel();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
_mainFrame = e.Parameter as Frame;
}
private void SaveButton_OnClick(object sender, RoutedEventArgs e)
{
ViewModel.Save();
_mainFrame.Navigate(typeof(MainPage10));
}
private async void SaveAsButton_OnClick(object sender, RoutedEventArgs e)
{
var savePicker = new FileSavePicker
{
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
SuggestedFileName = "New Database"
};
savePicker.FileTypeChoices.Add("KeePass 2.x database", new List<string> { ".kdbx" });
var file = await savePicker.PickSaveFileAsync();
if (file == null) return;
var token = StorageApplicationPermissions.FutureAccessList.Add(file);
ViewModel.Save(token);
_mainFrame.Navigate(typeof(MainPage10));
}
}
}

View File

@@ -0,0 +1,19 @@
<Page
x:Class="ModernKeePass.Views.WelcomePage"
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"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" Margin="0,10,0,10">
<SymbolIcon Symbol="Back" Margin="0,7,40,0" />
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" x:Uid="WelcomeOpen" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,6,0,10">
<SymbolIcon Symbol="Back" Margin="0,7,40,0" />
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" x:Uid="WelcomeNew" />
</StackPanel>
</StackPanel>
</Page>

View File

@@ -0,0 +1,15 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class WelcomePage
{
public WelcomePage()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,572 @@
<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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:local="using:ModernKeePass.Controls"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:ModernKeePass.Actions"
xmlns:userControls="using:ModernKeePass.Views.UserControls"
x:Name="PageRoot"
x:Class="ModernKeePass.Views.EntryDetailPage"
mc:Ignorable="d"
SizeChanged="EntryDetailPage_OnSizeChanged">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" />
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter" />
<converters:DoubleToSolidColorBrushConverter x:Key="DoubleToForegroungBrushComplexityConverter" />
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter" />
<converters:IconToSymbolConverter x:Key="IntToSymbolConverter" />
<Style TargetType="PasswordBox" x:Name="PasswordBoxWithButtonStyle">
<Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}" />
<Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}" />
<Setter Property="Foreground" Value="{ThemeResource TextBoxForegroundThemeBrush}" />
<Setter Property="Background" Value="{ThemeResource TextBoxBackgroundThemeBrush}" />
<Setter Property="SelectionHighlightColor" Value="{StaticResource MainColor}" />
<Setter Property="BorderBrush" Value="{ThemeResource TextBoxBorderThemeBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
<Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Grid>
<Grid.Resources>
<Style x:Name="RevealButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="BorderElement"
BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}"
BorderThickness="{TemplateBinding BorderThickness}"/>
<Border x:Name="BackgroundElement"
Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}"
Margin="{TemplateBinding BorderThickness}">
<TextBlock x:Name="GlyphElement"
Foreground="{ThemeResource TextBoxButtonForegroundThemeBrush}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontStyle="Normal"
Text="&#xE052;"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
AutomationProperties.AccessibilityView="Raw"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Name="GeneratorButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" >
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource MainColor}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPointerOverForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxButtonPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="BorderElement"
BorderBrush="{ThemeResource TextBoxButtonBorderThemeBrush}"
BorderThickness="{TemplateBinding BorderThickness}"/>
<Border x:Name="BackgroundElement"
Background="{ThemeResource TextBoxButtonBackgroundThemeBrush}"
Margin="{TemplateBinding BorderThickness}">
<TextBlock x:Name="GlyphElement"
Foreground="{ThemeResource TextBoxButtonForegroundThemeBrush}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontStyle="Normal"
Text="&#xE15E;"
Padding="4,0,4,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
AutomationProperties.AccessibilityView="Raw"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextBoxDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlBackgroundThemeOpacity}" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlBorderThemeOpacity}" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="BackgroundElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlPointerOverBackgroundThemeOpacity}" />
<DoubleAnimation Storyboard.TargetName="BorderElement"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="{ThemeResource TextControlPointerOverBorderThemeOpacity}" />
</Storyboard>
</VisualState>
<VisualState x:Name="Focused" />
</VisualStateGroup>
<VisualStateGroup x:Name="ButtonStates">
<VisualState x:Name="ButtonVisible">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RevealButton"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="ButtonCollapsed" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border x:Name="BackgroundElement"
Grid.Row="1"
Background="{TemplateBinding Background}"
Margin="{TemplateBinding BorderThickness}"
Grid.ColumnSpan="3"/>
<Border x:Name="BorderElement"
Grid.Row="1"
Grid.Column="0"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Grid.ColumnSpan="3"/>
<ContentPresenter
x:Name="HeaderContentPresenter"
Grid.Row="0"
Grid.Column="0"
Foreground="{ThemeResource TextBoxForegroundHeaderThemeBrush}"
Margin="0,4,0,4"
Grid.ColumnSpan="3"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontWeight="Semilight" />
<ScrollViewer x:Name="ContentElement"
Grid.Row="1"
Grid.Column="0"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsTabStop="False"
ZoomMode="Disabled"
AutomationProperties.AccessibilityView="Raw"/>
<ContentControl
x:Name="PlaceholderTextContentPresenter"
Grid.Row="1"
Grid.Column="0"
Foreground="{ThemeResource TextBoxPlaceholderTextThemeBrush}"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
IsTabStop="False"
Grid.ColumnSpan="3"
Content="{TemplateBinding PlaceholderText}"
IsHitTestVisible="False"/>
<Button x:Name="RevealButton"
Grid.Row="1"
Style="{StaticResource RevealButtonStyle}"
BorderThickness="{TemplateBinding BorderThickness}"
IsTabStop="False"
Grid.Column="1"
Visibility="Collapsed"
FontSize="{TemplateBinding FontSize}"
VerticalAlignment="Stretch"/>
<Button
Grid.Row="1"
Style="{StaticResource GeneratorButtonStyle}"
BorderThickness="{TemplateBinding BorderThickness}"
IsTabStop="False"
Grid.Column="2"
Visibility="Visible"
FontSize="{TemplateBinding FontSize}"
VerticalAlignment="Stretch" >
<Button.Flyout>
<Flyout>
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding Password}" ComparisonCondition="NotEqual" Value="" >
<!--<actions:CloseFlyoutAction />-->
<core:CallMethodAction MethodName="Hide" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
<StackPanel>
<TextBlock>
<Run x:Uid="PasswordGeneratorLength" />
<Run Text="{Binding PasswordLength}" />
</TextBlock>
<Slider Value="{Binding PasswordLength, Mode=TwoWay}" Margin="0,-10,0,-20" Style="{StaticResource MainColorSlider}"/>
<CheckBox IsChecked="{Binding UpperCasePatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorUpper" />
<CheckBox IsChecked="{Binding LowerCasePatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorLower" />
<CheckBox IsChecked="{Binding DigitsPatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorDigits" />
<CheckBox IsChecked="{Binding MinusPatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorMinus" />
<CheckBox IsChecked="{Binding UnderscorePatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorUnderscore" />
<CheckBox IsChecked="{Binding SpacePatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorSpace" />
<CheckBox IsChecked="{Binding SpecialPatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorSpecial" />
<CheckBox IsChecked="{Binding BracketsPatternSelected, Mode=TwoWay}" x:Uid="PasswordGeneratorBrackets" />
<TextBlock x:Uid="PasswordGeneratorAlso" Margin="0,5,0,0"/>
<TextBox Text="{Binding CustomChars, Mode=TwoWay}" />
<Button x:Uid="PasswordGeneratorButton" Command="{Binding GeneratePasswordCommand}" />
</StackPanel>
</Flyout>
</Button.Flyout>
<ToolTipService.ToolTip>
<ToolTip x:Uid="PasswordGeneratorTooltip" />
</ToolTipService.ToolTip>
</Button>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Page.DataContext>
<viewModels:EntryVm />
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="{StaticResource MenuGridLength}"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource MenuGridLength}" x:Name="LeftListViewColumn" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<userControls:HamburgerMenuUserControl x:Uid="HistoryLeftListView" ItemsSource="{Binding History}" ResizeTarget="{Binding ElementName=LeftListViewColumn}" SelectionChanged="HamburgerMenuUserControl_OnSelectionChanged" />
<Grid x:Name="StackPanel" Grid.Column="1">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Margin="20,0,0,20">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="0,20,0,0"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Margin" Value="0,20,0,0"/>
<Setter Property="FontSize" Value="18"/>
</Style>
<Style BasedOn="{StaticResource TextBoxWithButtonStyle}" TargetType="local:TextBoxWithButton" x:Key="EntryTextBoxWithButtonStyle">
<Setter Property="Width" Value="350"/>
<Setter Property="Height" Value="32"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
</StackPanel.Resources>
<TextBlock x:Uid="EntryLogin" />
<local:TextBoxWithButton x:Uid="LoginTextBox" Text="{Binding UserName, Mode=TwoWay}" Style="{StaticResource EntryTextBoxWithButtonStyle}" ButtonSymbol="&#xE16F;" IsEnabled="{Binding IsSelected}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{Binding UserName}" />
<actions:ToastAction x:Uid="ToastCopyLogin" Title="{Binding Name}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</local:TextBoxWithButton>
<TextBlock x:Uid="EntryPassword" />
<PasswordBox HorizontalAlignment="Left" Password="{Binding Password, Mode=TwoWay}" Width="350" Height="32" PasswordRevealMode="Peek" Visibility="{Binding IsRevealPassword, Converter={StaticResource InverseBooleanToVisibilityConverter}}" Style="{StaticResource PasswordBoxWithButtonStyle}" IsEnabled="{Binding IsSelected}" />
<local:TextBoxWithButton x:Uid="PasswordTextBox" Text="{Binding Password, Mode=TwoWay}" Visibility="{Binding IsRevealPassword, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource EntryTextBoxWithButtonStyle}" ButtonSymbol="&#xE16F;" IsEnabled="{Binding IsSelected}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{Binding Password}" />
<actions:ToastAction x:Uid="ToastCopyPassword" Title="{Binding Name}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</local:TextBoxWithButton>
<ProgressBar Value="{Binding PasswordComplexityIndicator, ConverterParameter=0\,128, Converter={StaticResource ProgressBarLegalValuesConverter}}" Maximum="128" Width="350" HorizontalAlignment="Left" Foreground="{Binding PasswordComplexityIndicator, ConverterParameter=128, Converter={StaticResource DoubleToForegroungBrushComplexityConverter}}" />
<CheckBox x:Uid="EntryShowPassword" HorizontalAlignment="Left" Margin="-3,0,0,0" IsChecked="{Binding IsRevealPassword, Mode=TwoWay}" IsEnabled="{Binding IsRevealPasswordEnabled}" />
<TextBlock TextWrapping="Wrap" Text="URL" FontSize="18"/>
<local:TextBoxWithButton x:Uid="UrlTextBox" Text="{Binding Url, Mode=TwoWay}" MaxLength="256" Style="{StaticResource EntryTextBoxWithButtonStyle}" ButtonSymbol="&#xE111;" IsEnabled="{Binding IsSelected}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:NavigateToUrlAction Url="{Binding Url}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</local:TextBoxWithButton>
<TextBlock x:Uid="EntryNotes" />
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Notes, Mode=TwoWay}" Width="350" Height="200" AcceptsReturn="True" IsSpellCheckEnabled="True" IsEnabled="{Binding IsSelected}" />
<CheckBox x:Uid="EntryExpirationDate" IsChecked="{Binding HasExpirationDate, Mode=TwoWay}" IsEnabled="{Binding IsSelected}" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<SymbolIcon Grid.Column="0" Symbol="Important" Foreground="DarkRed" Visibility="{Binding HasExpired, Converter={StaticResource BooleanToVisibilityConverter}}">
<ToolTipService.ToolTip>
<ToolTip x:Uid="EntryExpirationTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<StackPanel Grid.Column="1" x:Name="ExpirationDatePanel" Orientation="Horizontal" Visibility="{Binding HasExpirationDate, Converter={StaticResource BooleanToVisibilityConverter}}">
<DatePicker Margin="0,0,20,0" Date="{Binding ExpiryDate, Mode=TwoWay}" Style="{StaticResource MainColorDatePicker}" />
<TimePicker Time="{Binding ExpiryTime, Mode=TwoWay}" Style="{StaticResource MainColorTimePicker}" />
</StackPanel>
</Grid>
<StackPanel x:Name="EditDesign" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}" Orientation="Horizontal">
<StackPanel Width="250" HorizontalAlignment="Left">
<TextBlock x:Uid="EntryBackgroundColor" />
<userControls:ColorPickerUserControl SelectedColor="{Binding BackgroundColor, Converter={StaticResource ColorToBrushConverter}, Mode=TwoWay}" IsEnabled="{Binding IsSelected}" />
</StackPanel>
<StackPanel Width="250" HorizontalAlignment="Left">
<TextBlock x:Uid="EntryForegroundColor" />
<userControls:ColorPickerUserControl SelectedColor="{Binding ForegroundColor, Converter={StaticResource ColorToBrushConverter}, Mode=TwoWay}" IsEnabled="{Binding IsSelected}" />
</StackPanel>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Grid>
</Grid>
<!-- Bouton Précédent et titre de la page -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource MenuGridLength}"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Height="{StaticResource MenuSize}"
Width="{StaticResource MenuSize}"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<Grid Grid.Column="1" x:Name="TopGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}">
<userControls:SymbolPickerUserControl Width="80" Height="40" SelectedSymbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=0, Mode=TwoWay}" />
</Viewbox>
<Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
<SymbolIcon Symbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}}" Width="80" Height="40" />
</Viewbox>
<TextBox Grid.Column="1" Grid.Row="0"
Text="{Binding Name, Mode=TwoWay}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
Background="Transparent"
SelectionHighlightColor="{StaticResource MainColor}"
IsHitTestVisible="{Binding IsEditMode}"
BorderThickness="0"
FontSize="20"
FontWeight="Light"
TextWrapping="NoWrap"
VerticalAlignment="Center"
x:Uid="EntryTitle">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="True">
<actions:SetupFocusAction TargetObject="{Binding ElementName=TitleTextBox}" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<userControls:BreadCrumbUserControl Grid.Column="1" Grid.Row="1" ItemsSource="{Binding BreadCrumb}" Margin="5,-5,0,0" />
</Grid>
<userControls:TopMenuUserControl
x:Name="TopMenu" Grid.Column="2"
RestoreButtonVisibility="{Binding ParentGroup.IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}"
DeleteButtonVisibility="{Binding IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}"
IsEditButtonChecked="{Binding IsEditMode, Mode=TwoWay}"
SaveCommand="{Binding SaveCommand}"
RestoreCommand="{Binding UndoDeleteCommand}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="EditButtonClick">
<actions:SetupFocusAction TargetObject="{Binding ElementName=TitleTextBox}" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="DeleteButtonClick">
<actions:DeleteEntityAction Entity="{Binding}" Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="RestoreButtonClick">
<core:InvokeCommandAction Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}" />
<actions:ToastAction x:Uid="RestoreEntryCommand" Title="{Binding Name}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</userControls:TopMenuUserControl>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Small">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ExpirationDatePanel" Storyboard.TargetProperty="Orientation">
<DiscreteObjectKeyFrame KeyTime="0" Value="Vertical"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="EditDesign" Storyboard.TargetProperty="Orientation">
<DiscreteObjectKeyFrame KeyTime="0" Value="Vertical"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Large">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ExpirationDatePanel" Storyboard.TargetProperty="Orientation">
<DiscreteObjectKeyFrame KeyTime="0" Value="Horizontal"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="EditDesign" Storyboard.TargetProperty="Orientation">
<DiscreteObjectKeyFrame KeyTime="0" Value="Horizontal"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</Page>

View File

@@ -0,0 +1,78 @@
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.ViewModels;
// Pour en savoir plus sur le modèle d'élément Page Détail de l'élément, consultez la page http://go.microsoft.com/fwlink/?LinkId=234232
namespace ModernKeePass.Views
{
/// <summary>
/// Page affichant les détails d'un élément au sein d'un groupe, offrant la possibilité de
/// consulter les autres éléments qui appartiennent au même groupe.
/// </summary>
public sealed partial class EntryDetailPage
{
public EntryVm Model => (EntryVm) DataContext;
/// <summary>
/// NavigationHelper est utilisé sur chaque page pour faciliter la navigation et
/// gestion de la durée de vie des processus
/// </summary>
public NavigationHelper NavigationHelper { get; }
public EntryDetailPage()
{
InitializeComponent();
NavigationHelper = new NavigationHelper(this);
}
#region Inscription de NavigationHelper
/// Les méthodes fournies dans cette section sont utilisées simplement pour permettre
/// NavigationHelper pour répondre aux méthodes de navigation de la page.
///
/// La logique spécifique à la page doit être placée dans les gestionnaires d'événements pour
/// <see cref="Common.NavigationHelper.LoadState"/>
/// et <see cref="Common.NavigationHelper.SaveState"/>.
/// Le paramètre de navigation est disponible dans la méthode LoadState
/// en plus de l'état de page conservé durant une session antérieure.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
NavigationHelper.OnNavigatedTo(e);
if (!(e.Parameter is EntryVm)) return;
DataContext = (EntryVm)e.Parameter;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
NavigationHelper.OnNavigatedFrom(e);
Model.Reset();
}
#endregion
private void EntryDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
VisualStateManager.GoToState(this, e.NewSize.Width < 700 ? "Small" : "Large", true);
VisualStateManager.GoToState(TopMenu, e.NewSize.Width < 800 ? "Collapsed" : "Overflowed", true);
}
private void HamburgerMenuUserControl_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listView = sender as ListView;
switch (listView?.SelectedIndex)
{
case -1:
return;
default:
var entry = listView?.SelectedItem as EntryVm;
StackPanel.DataContext = entry;
TopGrid.DataContext = entry;
break;
}
}
}
}

View File

@@ -0,0 +1,326 @@
<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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:ModernKeePass.Actions"
xmlns:userControls="using:ModernKeePass.Views.UserControls"
x:Name="PageRoot"
x:Class="ModernKeePass.Views.GroupDetailPage"
mc:Ignorable="d"
SizeChanged="GroupDetailPage_OnSizeChanged">
<Page.Resources>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<converters:NullToBooleanConverter x:Key="NullToBooleanConverter"/>
<converters:IconToSymbolConverter x:Key="IntToSymbolConverter"/>
</Page.Resources>
<Page.DataContext>
<viewModels:GroupVm />
</Page.DataContext>
<Grid>
<Grid.Resources>
<CollectionViewSource
x:Name="EntriesViewSource"
Source="{Binding Entries}" />
<CollectionViewSource
x:Name="EntriesZoomedOutViewSource"
Source="{Binding EntriesZoomedOut}" IsSourceGrouped="True" />
</Grid.Resources>
<Grid.Background>
<StaticResource ResourceKey="ApplicationPageBackgroundThemeBrush"/>
</Grid.Background>
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="{StaticResource MenuGridLength}"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource MenuGridLength}" x:Name="LeftListViewColumn" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<userControls:HamburgerMenuUserControl x:Uid="GroupsLeftListView" ItemsSource="{Binding Groups}" SelectionChanged="groups_SelectionChanged" ButtonClicked="CreateGroup_ButtonClick" ResizeTarget="{Binding ElementName=LeftListViewColumn}" IsButtonVisible="{Binding IsSelected, Converter={StaticResource InverseBooleanToVisibilityConverter}}" />
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" x:Uid="ReorderEntriesLabel" Margin="10,10,0,0" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource BodyTextBlockStyle}" />
<!--<TextBlock Grid.Column="1" Grid.Row="0" x:Uid="EntrySymbol" Margin="40,20,0,0" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource BodyTextBlockStyle}" />-->
<HyperlinkButton Grid.Column="2" Grid.Row="0" VerticalAlignment="Top" Click="CreateEntry_ButtonClick" Visibility="{Binding IsSelected, Converter={StaticResource InverseBooleanToVisibilityConverter}}" HorizontalAlignment="Right" Foreground="{StaticResource MainColor}" Style="{StaticResource MainColorHyperlinkButton}">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Add">
<ToolTipService.ToolTip>
<ToolTip x:Uid="AddEntryTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock x:Name="AddEntryTextBlock" x:Uid="GroupCreateEntry" VerticalAlignment="Center" Margin="10,0,0,0" />
</StackPanel>
</HyperlinkButton>
<SemanticZoom Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" ScrollViewer.HorizontalScrollBarVisibility="Visible">
<SemanticZoom.ZoomedInView>
<!-- Horizontal scrolling grid -->
<GridView
x:Name="GridView"
ItemsSource="{Binding Source={StaticResource EntriesViewSource}}"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Entries"
TabIndex="1"
Margin="10,0,0,0"
SelectionChanged="entries_SelectionChanged"
IsSynchronizedWithCurrentItem="False"
BorderBrush="{StaticResource ListViewItemSelectedBackgroundThemeBrush}"
AllowDrop="True"
CanReorderItems="True"
CanDragItems="True"
DragItemsStarting="GridView_DragItemsStarting">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="False">
<actions:SetupFocusAction TargetObject="{Binding ElementName=GridView}" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
<GridView.ItemTemplate>
<DataTemplate>
<Grid Height="110" Width="480" x:Name="EntryGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="{Binding BackgroundColor, ConverterParameter={StaticResource MainColor}, Converter={StaticResource ColorToBrushConverter}}">
<Viewbox MaxHeight="50" Width="100">
<SymbolIcon Symbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=0}" Foreground="{StaticResource TextColor}" />
</Viewbox>
</Border>
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,10,0,0" >
<TextBlock x:Name="NameTextBlock" Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" />
<TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}" MaxHeight="60" />
<TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}" MaxHeight="60" />
</StackPanel>
<Button Grid.Column="2" Style="{StaticResource NoBorderButtonStyle}" Background="{StaticResource AppBarBackgroundThemeBrush}" VerticalAlignment="Bottom">
<SymbolIcon Symbol="More" />
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="EntryItemCopyLogin">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{Binding UserName}" />
<actions:ToastAction x:Uid="ToastCopyLogin" Title="{Binding Name}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemCopyPassword">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:ClipboardAction Text="{Binding Password}" />
<actions:ToastAction x:Uid="ToastCopyPassword" Title="{Binding Name}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="EntryItemCopyUrl">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<actions:NavigateToUrlAction Url="{Binding Url}" />
<actions:ToastAction x:Uid="ToastCopyUrl" Title="{Binding Name}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
</MenuFlyout>
</Button.Flyout>
</Button>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<GridView
ItemsSource="{Binding Source={StaticResource EntriesZoomedOutViewSource}}"
SelectionChanged="groups_SelectionChanged"
SelectionMode="None"
IsSynchronizedWithCurrentItem="False">
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Width="100" Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="LightGray" HorizontalAlignment="Left">
<TextBlock Text="{Binding Key}" Width="50" Margin="30" Foreground="{StaticResource MainColor}" Style="{StaticResource HeaderTextBlockStyle}" TextAlignment="Center" />
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</Grid>
</Grid>
<!-- Back button and page title -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{StaticResource MenuGridLength}"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Height="{StaticResource MenuSize}"
Width="{StaticResource MenuSize}"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<Grid Grid.Column="1" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}">
<userControls:SymbolPickerUserControl Width="80" Height="40" SelectedSymbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=48, Mode=TwoWay}" />
</Viewbox>
<Viewbox MaxHeight="30" Width="50" Grid.Column="0" Grid.Row="0" Visibility="{Binding IsEditMode, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
<SymbolIcon Symbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}}" Width="80" Height="40" />
</Viewbox>
<TextBox Grid.Column="1" Grid.Row="0"
x:Name="TitleTextBox"
Text="{Binding Name, Mode=TwoWay}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
SelectionHighlightColor="{StaticResource MainColor}"
Background="Transparent"
IsHitTestVisible="{Binding IsEditMode}"
BorderThickness="0"
FontSize="20"
FontWeight="Light"
TextWrapping="NoWrap"
VerticalAlignment="Center"
x:Uid="GroupTitle">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="True">
<actions:SetupFocusAction TargetObject="{Binding ElementName=TitleTextBox}" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<userControls:BreadCrumbUserControl Grid.Column="1" Grid.Row="1" ItemsSource="{Binding BreadCrumb}" Margin="5,-5,0,0" />
</Grid>
<userControls:TopMenuUserControl x:Name="TopMenu" Grid.Column="2"
RestoreButtonVisibility="{Binding ShowRestore, Converter={StaticResource BooleanToVisibilityConverter}}"
DeleteButtonVisibility="{Binding IsSelected, Converter={StaticResource InverseBooleanToVisibilityConverter}}"
SortButtonVisibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}"
IsEditButtonChecked="{Binding IsEditMode, Mode=TwoWay}"
IsDeleteButtonEnabled="{Binding IsNotRoot}"
SaveCommand="{Binding SaveCommand}"
RestoreCommand="{Binding UndoDeleteCommand}"
SortEntriesCommand="{Binding SortEntriesCommand}"
SortGroupsCommand="{Binding SortGroupsCommand}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="EditButtonClick">
<actions:SetupFocusAction TargetObject="{Binding ElementName=TitleTextBox}" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="DeleteButtonClick">
<actions:DeleteEntityAction Entity="{Binding}" Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="RestoreButtonClick">
<core:InvokeCommandAction Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}" />
<actions:ToastAction x:Uid="RestoreGroupCommand" Title="{Binding Name}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</userControls:TopMenuUserControl>
<Button Grid.Column="3" x:Name="SearchButton" Style="{StaticResource NoBorderButtonStyle}" Background="{ThemeResource ToggleButtonBackgroundThemeBrush}" Height="{StaticResource MenuSize}" Padding="25,0,25,0">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Find" />
<TextBlock x:Uid="SearchButtonLabel" x:Name="SearchButtonLabel" TextWrapping="NoWrap" FontSize="16" VerticalAlignment="Center" Margin="10,0,0,0" />
</StackPanel>
<ToolTipService.ToolTip>
<ToolTip x:Uid="SearchButtonTooltip" />
</ToolTipService.ToolTip>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:ChangePropertyAction TargetObject="{Binding ElementName=SearchBox}" PropertyName="Visibility" Value="Visible" />
<core:ChangePropertyAction TargetObject="{Binding ElementName=SearchButton}" PropertyName="Visibility" Value="Collapsed" />
<!-- TODO: make this work -->
<actions:SetupFocusAction TargetObject="{Binding ElementName=SearchBox}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Button>
<SearchBox Grid.Column="3" x:Uid="GroupSearch" x:Name="SearchBox" Padding="12" Width="350" Visibility="Collapsed" Background="{ThemeResource TextBoxDisabledBackgroundThemeBrush}" BorderThickness="0" Margin="0,5,0,5" FontSize="15" SuggestionsRequested="SearchBox_OnSuggestionsRequested" SearchHistoryEnabled="False" ResultSuggestionChosen="SearchBox_OnResultSuggestionChosen" Style="{StaticResource MainColorSearchBox}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="LostFocus">
<core:ChangePropertyAction TargetObject="{Binding ElementName=SearchBox}" PropertyName="Visibility" Value="Collapsed" />
<core:ChangePropertyAction TargetObject="{Binding ElementName=SearchButton}" PropertyName="Visibility" Value="Visible" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</SearchBox>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="DragDropGroup">
<VisualState x:Name="Dragging">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GridView" Storyboard.TargetProperty="BorderThickness">
<DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Dropped">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GridView" Storyboard.TargetProperty="BorderThickness">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="TopMenuGroup">
<VisualState x:Name="Small">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="AddEntryTextBlock" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchButtonLabel" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Large">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="AddEntryTextBlock" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SearchButtonLabel" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</Page>

View File

@@ -0,0 +1,142 @@
using System;
using System.Linq;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.Events;
using ModernKeePass.ViewModels;
// The Group Detail Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234229
namespace ModernKeePass.Views
{
/// <summary>
/// A page that displays an overview of a single group, including a preview of the items
/// within the group.
/// </summary>
public sealed partial class GroupDetailPage
{
/// <summary>
/// NavigationHelper is used on each page to aid in navigation and
/// process lifetime management
/// </summary>
public NavigationHelper NavigationHelper { get; }
public GroupVm Model => (GroupVm)DataContext;
public GroupDetailPage()
{
InitializeComponent();
NavigationHelper = new NavigationHelper(this);
}
#region NavigationHelper registration
/// The methods provided in this section are simply used to allow
/// NavigationHelper to respond to the page's navigation methods.
///
/// Page specific logic should be placed in event handlers for the
/// <see cref="Common.NavigationHelper.LoadState"/>
/// and <see cref="Common.NavigationHelper.SaveState"/>.
/// The navigation parameter is available in the LoadState method
/// in addition to page state preserved during an earlier session.
///
protected override void OnNavigatedTo(NavigationEventArgs e)
{
NavigationHelper.OnNavigatedTo(e);
if (e.Parameter is PasswordEventArgs args)
DataContext = args.RootGroup;
else
{
if (e.Parameter is GroupVm vm)
DataContext = vm;
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
NavigationHelper.OnNavigatedFrom(e);
}
#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 GroupVm;
Frame.Navigate(typeof(GroupDetailPage), group);
break;
}
}
private void entries_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
switch (GridView.SelectedIndex)
{
case -1:
return;
default:
var entry = GridView.SelectedItem as EntryVm;
Frame.Navigate(typeof(EntryDetailPage), entry);
break;
}
}
private void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)
{
// We need to synchronize the two lists (zoomed-in and zoomed-out) because the source is different
if (!e.IsSourceZoomedInView)
{
e.DestinationItem.Item = e.SourceItem.Item;
}
}
private void CreateEntry_ButtonClick(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(EntryDetailPage), Model.AddNewEntry());
}
private void CreateGroup_ButtonClick(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(GroupDetailPage), Model.AddNewGroup());
}
private void GridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
e.Cancel = !Model.IsEditMode;
e.Data.RequestedOperation = DataPackageOperation.Move;
}
private void SearchBox_OnSuggestionsRequested(SearchBox sender, SearchBoxSuggestionsRequestedEventArgs args)
{
var imageUri = RandomAccessStreamReference.CreateFromUri(new Uri("ms-appdata://Assets/ModernKeePass-SmallLogo.scale-80.png"));
var results = Model.SubEntries.Where(e => e.Name.IndexOf(args.QueryText, StringComparison.OrdinalIgnoreCase) >= 0).Take(5);
foreach (var result in results)
{
args.Request.SearchSuggestionCollection.AppendResultSuggestion(result.Name, result.ParentGroup.Name, result.Id, imageUri, string.Empty);
}
}
private void SearchBox_OnResultSuggestionChosen(SearchBox sender, SearchBoxResultSuggestionChosenEventArgs args)
{
var entry = Model.SubEntries.FirstOrDefault(e => e.Id == args.Tag);
Frame.Navigate(typeof(EntryDetailPage), entry);
}
private void GroupDetailPage_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
VisualStateManager.GoToState(this, e.NewSize.Width < 800 ? "Small" : "Large", true);
VisualStateManager.GoToState(TopMenu, e.NewSize.Width < 800 ? "Collapsed" : "Overflowed", true);
}
#endregion
}
}

View File

@@ -0,0 +1,148 @@
<basePages:LayoutAwarePageBase
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:viewModels="using:ModernKeePass.ViewModels"
xmlns:controls="using:ModernKeePass.Controls"
xmlns:basePages="using:ModernKeePass.Views.BasePages"
x:Class="ModernKeePass.Views.MainPage"
x:Name="PageRoot"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource
x:Name="MenuItemsSource"
Source="{Binding MainMenuItems}"
IsSourceGrouped="True" />
</Page.Resources>
<Page.Background>
<StaticResource ResourceKey="ApplicationPageBackgroundThemeBrush"/>
</Page.Background>
<Page.DataContext>
<viewModels:MainVm />
</Page.DataContext>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="{StaticResource MenuGridLength}"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="PrimaryColumn" Width="250" />
<ColumnDefinition x:Name="SecondaryColumn" Width="*" />
</Grid.ColumnDefinitions>
<!-- Back button and page title -->
<Grid x:Name="TitlePanel" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="BackButton"
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Visibility="Collapsed"
Height="{StaticResource MenuSize}"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<TextBlock x:Name="TitleTextBox" Text="{Binding Name}" Grid.Column="1" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<controls:ListViewWithDisable
Grid.Column="0"
Grid.Row="1"
x:Name="MenuListView"
SelectionChanged="ListView_SelectionChanged"
Background="{ThemeResource AppBarBackgroundThemeBrush}"
ItemsSource="{Binding Source={StaticResource MenuItemsSource}}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<controls:ListViewWithDisable.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding SymbolIcon}" />
<TextBlock Text="{Binding Title}" Margin="10,5,0,0" />
</StackPanel>
</DataTemplate>
</controls:ListViewWithDisable.ItemTemplate>
<controls:ListViewWithDisable.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="DarkGray" Margin="20,0,0,0">
<Border Height="1" Width="300" HorizontalAlignment="Stretch"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</controls:ListViewWithDisable.GroupStyle>
</controls:ListViewWithDisable>
<TextBlock x:Name="PageTitleTextBlock" Grid.Column="1" Grid.Row="0" FontSize="24" VerticalAlignment="Center" Margin="10,0,0,0" >
<Run Text="{Binding SelectedItem}" />
</TextBlock>
<Frame x:Name="MenuFrame" Grid.Column="1" Grid.Row="1" Margin="0,10,0,0" />
<VisualStateManager.VisualStateGroups>
<!-- Visual states reflect the application's view state -->
<VisualStateGroup x:Name="ViewStates">
<VisualState x:Name="PrimaryView" />
<VisualState x:Name="SinglePane">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PrimaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="*"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SecondaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuFrame" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PageTitleTextBlock" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuListView" Storyboard.TargetProperty="Padding">
<DiscreteObjectKeyFrame KeyTime="0" Value="120,0,90,60"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!--
When an item is selected and only one pane is shown the details display requires more extensive changes:
* Hide the master list and the column it was in
* Move item details down a row to make room for the title
* Move the title directly above the details
* Adjust padding for details
-->
<VisualState x:Name="SinglePane_Detail">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PrimaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuListView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitlePanel" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleTextBox" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PageTitleTextBlock" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="60,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</basePages:LayoutAwarePageBase>

View File

@@ -0,0 +1,40 @@
using Windows.Storage;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.ViewModels;
using ModernKeePass.ViewModels.ListItems;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage
{
public new MainVm Model => (MainVm)DataContext;
public MainPage()
{
InitializeComponent();
ListView = MenuListView;
ListViewSource = MenuItemsSource;
}
private new void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
base.ListView_SelectionChanged(sender, e);
if (!(Model.SelectedItem is MainMenuItemViewModel selectedItem)) MenuFrame.Navigate(typeof(WelcomePage));
else selectedItem.Destination.Navigate(selectedItem.PageType, selectedItem.Parameter);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var file = e.Parameter as StorageFile;
DataContext = new MainVm(Frame, MenuFrame, file);
}
}
}

View File

@@ -0,0 +1,147 @@
<basePages:LayoutAwarePageBase
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:controls="using:ModernKeePass.Controls"
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:basePages="using:ModernKeePass.Views.BasePages"
x:Name="PageRoot"
x:Class="ModernKeePass.Views.SettingsPage"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource x:Name="MenuItemsSource" Source="{Binding MenuItems}" IsSourceGrouped="True" />
</Page.Resources>
<Page.DataContext>
<viewModels:SettingsVm />
</Page.DataContext>
<Page.Background>
<StaticResource ResourceKey="ApplicationPageBackgroundThemeBrush"/>
</Page.Background>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="PrimaryColumn" Width="250" />
<ColumnDefinition x:Name="SecondaryColumn" Width="*" />
</Grid.ColumnDefinitions>
<!-- Back button and page title -->
<Grid x:Name="TitlePanel" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="BackButton"
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Height="50"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<TextBlock x:Name="TitleTextBox" x:Uid="SettingsTitle" Grid.Column="1" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<controls:ListViewWithDisable
Grid.Column="0"
Grid.Row="1"
x:Name="MenuListView"
SelectionChanged="ListView_SelectionChanged"
Background="{ThemeResource AppBarBackgroundThemeBrush}"
ItemsSource="{Binding Source={StaticResource MenuItemsSource}}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="False"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<controls:ListViewWithDisable.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding SymbolIcon}" />
<TextBlock Text="{Binding Title}" Margin="10,5,0,0" />
</StackPanel>
</DataTemplate>
</controls:ListViewWithDisable.ItemTemplate>
<controls:ListViewWithDisable.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel Margin="20,0,0,0">
<TextBlock Text="{Binding Key}" />
<Grid Background="DarkGray">
<Border Height="1" Width="300" HorizontalAlignment="Stretch"/>
</Grid>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</controls:ListViewWithDisable.GroupStyle>
</controls:ListViewWithDisable>
<TextBlock x:Name="PageTitleTextBlock" Grid.Column="1" Grid.Row="0" FontSize="24" VerticalAlignment="Center" Margin="10,0,0,0" >
<Run Text="{Binding SelectedItem}" />
</TextBlock>
<Frame x:Name="MenuFrame" Grid.Column="1" Grid.Row="1" Margin="0,10,0,0" />
<VisualStateManager.VisualStateGroups>
<!-- Visual states reflect the application's view state -->
<VisualStateGroup x:Name="ViewStates">
<VisualState x:Name="PrimaryView" />
<VisualState x:Name="SinglePane">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PrimaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="*"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SecondaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuFrame" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PageTitleTextBlock" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuListView" Storyboard.TargetProperty="Padding">
<DiscreteObjectKeyFrame KeyTime="0" Value="120,0,90,60"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!--
When an item is selected and only one pane is shown the details display requires more extensive changes:
* Hide the master list and the column it was in
* Move item details down a row to make room for the title
* Move the title directly above the details
* Adjust padding for details
-->
<VisualState x:Name="SinglePane_Detail">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PrimaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuListView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitlePanel" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleTextBox" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PageTitleTextBlock" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="60,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</basePages:LayoutAwarePageBase>

View File

@@ -0,0 +1,31 @@
using Windows.UI.Xaml.Controls;
using ModernKeePass.ViewModels;
using ModernKeePass.ViewModels.ListItems;
// The Split Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234234
namespace ModernKeePass.Views
{
/// <summary>
/// A page that displays a group title, a list of items within the group, and details for
/// the currently selected item.
/// </summary>
public sealed partial class SettingsPage
{
public new SettingsViewModel Model => (SettingsViewModel)DataContext;
public SettingsPage()
{
InitializeComponent();
ListView = MenuListView;
ListViewSource = MenuItemsSource;
}
private new void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
base.ListView_SelectionChanged(sender, e);
var selectedItem = Model.SelectedItem as ListMenuItemViewModel;
MenuFrame?.Navigate(selectedItem == null ? typeof(SettingsWelcomePage) : selectedItem.PageType);
}
}
}

View File

@@ -0,0 +1,33 @@
<Page
x:Class="ModernKeePass.Views.SettingsPage10"
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"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid x:Name="AppTitleBar" Background="Transparent" />
<NavigationView
x:Name="NavigationView"
x:Uid="SettingsMenu"
IsSettingsVisible="False"
ItemInvoked="NavigationView_OnItemInvoked"
Loaded="NavigationView_OnLoaded"
BackRequested="NavigationView_OnBackRequested">
<NavigationView.MenuItems>
<NavigationViewItem x:Name="Welcome" Tag="welcome" Visibility="Collapsed" />
<NavigationViewItemHeader x:Uid="SettingsMenuGroupApplication" />
<NavigationViewItem x:Uid="SettingsMenuItemNew" Icon="Add" Tag="new" />
<NavigationViewItem x:Uid="SettingsMenuItemSave" Icon="Save" Tag="save" />
<NavigationViewItemSeparator/>
<NavigationViewItemHeader x:Uid="SettingsMenuGroupDatabase" />
<NavigationViewItem x:Uid="SettingsMenuItemGeneral" Icon="Setting" IsEnabled="{x:Bind ViewModel.IsDatabaseOpened, Mode=OneTime}" Tag="general" />
<NavigationViewItem x:Uid="SettingsMenuItemSecurity" Icon="Permissions" IsEnabled="{x:Bind ViewModel.IsDatabaseOpened, Mode=OneTime}" Tag="security" />
</NavigationView.MenuItems>
<Frame x:Name="ContentFrame" Margin="24"/>
</NavigationView>
</Grid>
</Page>

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.ApplicationModel.Core;
using ModernKeePass.ViewModels;
using ModernKeePass.Views.SettingsPageFrames;
namespace ModernKeePass.Views
{
public partial class SettingsPage10
{
private SettingsViewModel ViewModel { get; }
private readonly IList<(string Tag, Type Page)> _pages = new List<(string Tag, Type Page)>
{
("welcome", typeof(SettingsWelcomePage)),
("new", typeof(SettingsNewDatabasePage)),
("save", typeof(SettingsSavePage)),
("general", typeof(SettingsDatabasePage)),
("security", typeof(SettingsSecurityPage))
};
public SettingsPage10()
{
InitializeComponent();
ViewModel = new SettingsViewModel();
SetTitleBar();
}
private void SetTitleBar()
{
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
coreTitleBar.ExtendViewIntoTitleBar = true;
Window.Current.SetTitleBar(AppTitleBar);
}
private void NavigationView_OnItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{
// Getting the Tag from Content (args.InvokedItem is the content of NavigationViewItem)
var navItem = NavigationView.MenuItems
.OfType<NavigationViewItem>()
.First(i => args.InvokedItem.Equals(i.Content));
NavigationView_Navigate(navItem);
}
private void NavigationView_Navigate(NavigationViewItem navItem)
{
var item = _pages.First(p => p.Tag.Equals(navItem.Tag));
NavigationView.Header = navItem.Content;
ContentFrame.Navigate(item.Page);
}
private void NavigationView_OnLoaded(object sender, RoutedEventArgs e)
{
NavigationView_Navigate(Welcome);
NavigationView.IsBackEnabled = Frame.CanGoBack;
}
private void NavigationView_OnBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
{
Frame.GoBack();
}
}
}

View File

@@ -0,0 +1,23 @@
<Page
x:Class="ModernKeePass.Views.SettingsPageFrames.SettingsDatabasePage"
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"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleSwitch x:Uid="SettingsDatabaseRecycleBin" IsOn="{x:Bind ViewModel.HasRecycleBin, Mode=TwoWay}" Style="{StaticResource MainColorToggleSwitch}" />
<StackPanel Visibility="{x:Bind ViewModel.HasRecycleBin}">
<RadioButton x:Uid="SettingsDatabaseRecycleBinCreate" GroupName="Recycle" IsChecked="{x:Bind ViewModel.IsNewRecycleBin, Mode=TwoWay}" />
<RadioButton x:Name="RadioButton" x:Uid="SettingsDatabaseRecycleBinExisting" GroupName="Recycle" IsChecked="{x:Bind ViewModel.HasRecycleBin}" />
<ComboBox ItemsSource="{x:Bind ViewModel.Groups}" SelectedItem="{x:Bind ViewModel.SelectedRecycleBin, Mode=TwoWay}" IsEnabled="{Binding IsChecked, ElementName=RadioButton}" />
</StackPanel>
<TextBlock x:Uid="SettingsDatabaseEncryption" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />
<ComboBox ItemsSource="{x:Bind ViewModel.Ciphers}" SelectedItem="{x:Bind ViewModel.SelectedCipher, Mode=TwoWay}" ItemContainerStyle="{StaticResource MainColorComboBoxItem}" Style="{StaticResource MainColorComboBox}" />
<TextBlock x:Uid="SettingsDatabaseCompression" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />
<ComboBox ItemsSource="{x:Bind ViewModel.Compressions}" SelectedItem="{x:Bind ViewModel.SelectedCompression, Mode=TwoWay}" ItemContainerStyle="{StaticResource MainColorComboBoxItem}" Style="{StaticResource MainColorComboBox}" />
<TextBlock x:Uid="SettingsDatabaseKdf" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />
<ComboBox ItemsSource="{x:Bind ViewModel.KeyDerivations}" SelectedItem="{x:Bind ViewModel.SelectedKeyDerivation, Mode=TwoWay}" ItemContainerStyle="{StaticResource MainColorComboBoxItem}" Style="{StaticResource MainColorComboBox}" />
</StackPanel>
</Page>

View File

@@ -0,0 +1,20 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
using ModernKeePass.ViewModels.ListItems;
namespace ModernKeePass.Views.SettingsPageFrames
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class SettingsDatabasePage
{
public SettingsDatabaseViewModel ViewModel { get; }
public SettingsDatabasePage()
{
InitializeComponent();
ViewModel = new SettingsDatabaseViewModel();
}
}
}

View File

@@ -0,0 +1,37 @@
<Page
x:Class="ModernKeePass.Views.SettingsPageFrames.SettingsNewDatabasePage"
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"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Uid="SettingsNewDatabaseDesc" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,0,0,10"/>
<ToggleSwitch x:Uid="SettingsNewDatabaseSample" IsOn="{x:Bind ViewModel.IsCreateSample, Mode=TwoWay}" Style="{StaticResource MainColorToggleSwitch}" />
<TextBlock x:Uid="SettingsNewDatabaseKdf" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="1"
ItemsSource="{x:Bind ViewModel.FileFormats}"
SelectedItem="{x:Bind ViewModel.FileFormatVersion, Mode=TwoWay}"
ItemContainerStyle="{StaticResource MainColorComboBoxItem}"
Style="{StaticResource MainColorComboBox}" />
<Button Grid.Column="2" Style="{StaticResource TextBlockButtonStyle}">
<SymbolIcon Symbol="Help" RenderTransformOrigin="0.5,0.5" >
<SymbolIcon.RenderTransform>
<CompositeTransform ScaleX="0.7" ScaleY="0.7"/>
</SymbolIcon.RenderTransform>
</SymbolIcon>
<Button.Flyout>
<Flyout>
<TextBlock x:Uid="SettingsNewDatabaseKdfMoreInfo" TextWrapping="WrapWholeWords" MaxWidth="400" />
</Flyout>
</Button.Flyout>
</Button>
</Grid>
</StackPanel>
</Page>

View File

@@ -0,0 +1,20 @@
// Pour en savoir plus sur le modèle d'élément Page vierge, consultez la page http://go.microsoft.com/fwlink/?LinkId=234238
using ModernKeePass.ViewModels.ListItems;
namespace ModernKeePass.Views.SettingsPageFrames
{
/// <summary>
/// Une page vide peut être utilisée seule ou constituer une page de destination au sein d'un frame.
/// </summary>
public sealed partial class SettingsNewDatabasePage
{
private SettingsNewViewModel ViewModel { get; }
public SettingsNewDatabasePage()
{
InitializeComponent();
ViewModel = new SettingsNewViewModel();
}
}
}

View File

@@ -0,0 +1,14 @@
<Page
x:Class="ModernKeePass.Views.SettingsPageFrames.SettingsSavePage"
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"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Uid="SettingsSaveDatabaseSuspendTitle" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,0,0,10"/>
<TextBlock x:Uid="SettingsSaveDatabaseSuspendDesc" TextWrapping="WrapWholeWords" Margin="5,0,0,10"/>
<ToggleSwitch x:Uid="SettingsSaveDatabaseSuspend" IsOn="{x:Bind ViewModel.IsSaveSuspend, Mode=TwoWay}" Style="{StaticResource MainColorToggleSwitch}" />
</StackPanel>
</Page>

View File

@@ -0,0 +1,19 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
using ModernKeePass.ViewModels.ListItems;
namespace ModernKeePass.Views.SettingsPageFrames
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class SettingsSavePage
{
private SettingsSaveViewModel ViewModel { get; }
public SettingsSavePage()
{
InitializeComponent();
ViewModel = new SettingsSaveViewModel();
}
}
}

View File

@@ -0,0 +1,28 @@
<Page
x:Class="ModernKeePass.Views.SettingsPageFrames.SettingsSecurityPage"
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:userControls="using:ModernKeePass.Views.UserControls"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:actions="using:ModernKeePass.Actions"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Uid="SettingsSecurityTitle" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,0,0,0" />
<TextBlock TextWrapping="WrapWholeWords" Margin="5,0,0,0">
<Run x:Uid="SettingsSecurityDesc1" />
<Run x:Uid="SettingsSecurityDesc2" FontWeight="SemiBold" />
<Run x:Uid="SettingsSecurityDesc3" />
</TextBlock>
<userControls:UpdateCredentialsUserControl Margin="0,20,0,0" x:Uid="SettingsSecurityUpdateButton">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ValidationChecked">
<actions:ToastAction x:Uid="ToastUpdateDatabase" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</userControls:UpdateCredentialsUserControl>
</StackPanel>
</Page>

View File

@@ -0,0 +1,15 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views.SettingsPageFrames
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class SettingsSecurityPage
{
public SettingsSecurityPage()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,14 @@
<Page
x:Class="ModernKeePass.Views.SettingsPageFrames.SettingsWelcomePage"
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"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock FontSize="24" VerticalAlignment="Center" Margin="10,-70,0,0" x:Uid="SettingsWelcomeTitle" />
<TextBlock VerticalAlignment="Center" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,0,0,0" x:Uid="SettingsWelcomeDesc" />
<TextBlock VerticalAlignment="Center" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,0,0,0" x:Uid="SettingsWelcomeHowto" />
</StackPanel>
</Page>

View File

@@ -0,0 +1,15 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
namespace ModernKeePass.Views.SettingsPageFrames
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class SettingsWelcomePage
{
public SettingsWelcomePage()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,46 @@
<UserControl x:Name="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:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors"
mc:Ignorable="d">
<ItemsControl ItemsSource="{Binding ItemsSource, ElementName=UserControl}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate x:Name="FirstItemTemplate">
<HyperlinkButton Foreground="{StaticResource MainColor}" Content="{Binding Name}" Style="{StaticResource MainColorHyperlinkButton}" FontWeight="Light" FontSize="12" Padding="0">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction Parameter="{Binding}" TargetPage="ModernKeePass.Views.GroupDetailPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</HyperlinkButton>
</DataTemplate>
<DataTemplate x:Name="OtherItemsTemplate">
<StackPanel Orientation="Horizontal">
<Viewbox MaxHeight="10" Margin="0,2,0,0">
<SymbolIcon Symbol="Forward" />
</Viewbox>
<HyperlinkButton Foreground="{StaticResource MainColor}" Content="{Binding Name}" Style="{StaticResource MainColorHyperlinkButton}" FontWeight="Light" FontSize="12" Padding="0">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction Parameter="{Binding}" TargetPage="ModernKeePass.Views.GroupDetailPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</HyperlinkButton>
</StackPanel>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemTemplateSelector>
<templateSelectors:FirstItemDataTemplateSelector FirstItem="{StaticResource FirstItemTemplate}" OtherItem="{StaticResource OtherItemsTemplate}"/>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
</UserControl>

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using Windows.UI.Xaml;
using ModernKeePass.Domain.Entities;
// 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();
}
public IEnumerable<Entity> ItemsSource
{
get => (IEnumerable<Entity>)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",
typeof(IEnumerable<Entity>),
typeof(BreadCrumbUserControl),
new PropertyMetadata(new Stack<Entity>(), (o, args) => { }));
}
}

View File

@@ -0,0 +1,24 @@
<UserControl x:Name="UserControl"
x:Class="ModernKeePass.Views.UserControls.ColorPickerUserControl"
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"
mc:Ignorable="d">
<ComboBox x:Name="ComboBox"
Width="350"
Height="35"
ItemsSource="{Binding Colors, ElementName=UserControl}"
SelectionChanged="Selector_OnSelectionChanged"
Loaded="ComboBox_Loaded" ItemContainerStyle="{StaticResource MainColorComboBoxItem}"
Style="{StaticResource MainColorComboBox}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,10,0">
<TextBlock Width="100" Text="{Binding ColorName}" Margin="0,0,10,0" />
<Rectangle Width="100" Fill="{Binding ColorBrush}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</UserControl>

View File

@@ -0,0 +1,61 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
namespace ModernKeePass.Views.UserControls
{
public sealed partial class ColorPickerUserControl
{
public struct Color
{
public string ColorName { get; set; }
public SolidColorBrush ColorBrush { get; set; }
}
public List<Color> Colors { get; }
public SolidColorBrush SelectedColor
{
get => (SolidColorBrush)GetValue(SelectedColorProperty);
set => SetValue(SelectedColorProperty, value);
}
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register(
"SelectedColor",
typeof(SolidColorBrush),
typeof(ColorPickerUserControl),
new PropertyMetadata(new SolidColorBrush(), (o, args) => { }));
public ColorPickerUserControl()
{
InitializeComponent();
Colors = new List<Color>();
var type = typeof(Windows.UI.Colors);
var properties = type.GetRuntimeProperties().ToArray();
foreach (var propertyInfo in properties)
{
Colors.Add(new Color
{
ColorName = propertyInfo.Name,
ColorBrush = new SolidColorBrush((Windows.UI.Color)propertyInfo.GetValue(null, null))
});
}
}
private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
ComboBox.SelectedItem = Colors.Find(c => c.ColorBrush.Color.Equals(SelectedColor.Color));
}
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedItem = ComboBox.SelectedItem as Color? ?? new Color();
SelectedColor = selectedItem.ColorBrush;
}
}
}

View File

@@ -0,0 +1,55 @@
<UserControl
x:Class="ModernKeePass.Views.UserControls.CredentialsUserControl"
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:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:viewModels="using:ModernKeePass.ViewModels"
mc:Ignorable="d" >
<UserControl.Resources>
<converters:DiscreteIntToSolidColorBrushConverter x:Key="DiscreteIntToSolidColorBrushConverter"/>
<converters:EmptyStringToVisibilityConverter x:Key="EmptyStringToVisibilityConverter"/>
</UserControl.Resources>
<Grid x:Name="Grid">
<!-- DataContext is not set at the root of the control because of issues happening when displaying it -->
<Grid.DataContext>
<viewModels:CredentialsViewModel />
</Grid.DataContext>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="45" />
<RowDefinition Height="40" />
<RowDefinition Height="40" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" IsChecked="{x:Bind ViewModel.HasPassword, Mode=TwoWay}" />
<PasswordBox Grid.Row="0" Grid.Column="1" x:Uid="CompositeKeyPassword" Password="{x:Bind ViewModel.Password, Mode=TwoWay}" Height="30" PasswordRevealMode="Peek" KeyDown="PasswordBox_KeyDown" BorderBrush="{x:Bind ViewModel.StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" SelectionHighlightColor="{StaticResource MainColor}" >
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="GotFocus">
<core:ChangePropertyAction TargetObject="{Binding}" PropertyName="HasPassword" Value="True" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</PasswordBox>
<CheckBox Grid.Row="1" Grid.Column="0" IsChecked="{x:Bind ViewModel.HasKeyFile, Mode=TwoWay}" />
<HyperlinkButton Grid.Row="1" Grid.Column="1" Margin="-15,0,0,0"
Content="{x:Bind ViewModel.KeyFileText, Mode=OneWay}"
IsEnabled="{x:Bind ViewModel.HasKeyFile, Mode=OneWay}"
Click="KeyFileButton_Click"
Style="{StaticResource MainColorHyperlinkButton}" />
<Button Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2"
x:Uid="CredentialsOkButton"
Click="OpenButton_OnClick"
Style="{StaticResource MainColorButton}"
IsEnabled="{x:Bind ViewModel.IsValid, Mode=OneWay}" />
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="3" Height="Auto" FontSize="14" FontWeight="Light"
Text="{x:Bind ViewModel.Status, Mode=OneWay}"
Foreground="{x:Bind ViewModel.StatusType, Mode=OneWay, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}"
Visibility="{x:Bind ViewModel.Status, Mode=OneWay, Converter={StaticResource EmptyStringToVisibilityConverter}}" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Storage.AccessCache;
using Windows.Storage.Pickers;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
using Autofac;
using ModernKeePass.Common;
using ModernKeePass.Domain.Dtos;
using ModernKeePass.Domain.Interfaces;
using ModernKeePass.Extensions;
using ModernKeePass.ViewModels;
// Pour en savoir plus sur le modèle d'élément Contrôle utilisateur, consultez la page http://go.microsoft.com/fwlink/?LinkId=234236
namespace ModernKeePass.Views.UserControls
{
public sealed partial class CredentialsUserControl
{
private readonly IDatabaseService _databaseService;
private readonly IResourceService _resourceService;
public CredentialsViewModel ViewModel => Grid.DataContext as CredentialsViewModel;
public string DatabaseFilePath
{
get => (string)GetValue(DatabaseFilePathProperty);
set => SetValue(DatabaseFilePathProperty, value);
}
public static readonly DependencyProperty DatabaseFilePathProperty =
DependencyProperty.Register(
"DatabaseFilePath",
typeof(string),
typeof(CredentialsUserControl),
new PropertyMetadata(null, (o, args) => { }));
public event EventHandler ValidationChecking;
public event EventHandler ValidationChecked;
public CredentialsUserControl(): this(App.Container.Resolve<IDatabaseService>(), App.Container.Resolve<IResourceService>())
{ }
public CredentialsUserControl(IDatabaseService databaseService, IResourceService resourceService)
{
InitializeComponent();
_databaseService = databaseService;
_resourceService = resourceService;
}
private async void OpenButton_OnClick(object sender, RoutedEventArgs e)
{
ValidationChecking?.Invoke(this, new EventArgs());
if (_databaseService.IsOpen)
{
await MessageDialogHelper.ShowActionDialog(_resourceService.GetResourceValue("MessageDialogDBOpenTitle"),
string.Format(_resourceService.GetResourceValue("MessageDialogDBOpenDesc"), _databaseService.Name),
_resourceService.GetResourceValue("MessageDialogDBOpenButtonSave"),
_resourceService.GetResourceValue("MessageDialogDBOpenButtonDiscard"),
async command =>
{
await _databaseService.Save();
ToastNotificationHelper.ShowGenericToast(
_databaseService.Name,
_resourceService.GetResourceValue("ToastSavedMessage"));
_databaseService.Close();
await OpenDatabase();
},
async command =>
{
_databaseService.Close();
await OpenDatabase();
});
}
else
{
await OpenDatabase();
}
}
private void PasswordBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Enter && ViewModel.IsValid)
{
OpenButton_OnClick(sender, e);
// Stop the event from triggering twice
e.Handled = true;
}
}
private async void KeyFileButton_Click(object sender, RoutedEventArgs e)
{
var picker =
new FileOpenPicker
{
ViewMode = PickerViewMode.List,
SuggestedStartLocation = PickerLocationId.DocumentsLibrary
};
picker.FileTypeFilter.Add(".key");
// Application now has read/write access to the picked file
var file = await picker.PickSingleFileAsync();
if (file == null) return;
var token = StorageApplicationPermissions.FutureAccessList.Add(file);
ViewModel.KeyFilePath = token;
}
private async Task OpenDatabase()
{
if (await Dispatcher.RunTaskAsync(async () =>
{
var fileInfo = new FileInfo
{
Path = DatabaseFilePath
};
return await ViewModel.OpenDatabase(fileInfo);
}))
{
ValidationChecked?.Invoke(this, new EventArgs());
}
}
}
}

View File

@@ -0,0 +1,119 @@
<UserControl x:Name="UserControl"
x:Class="ModernKeePass.Views.UserControls.HamburgerMenuUserControl"
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:templateSelectors="using:ModernKeePass.TemplateSelectors"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:converters="using:ModernKeePass.Converters"
mc:Ignorable="d">
<UserControl.Resources>
<converters:IconToSymbolConverter x:Key="IntToSymbolConverter"/>
</UserControl.Resources>
<ListView
ItemsSource="{Binding ItemsSource, ElementName=UserControl}"
SelectionChanged="Selector_OnSelectionChanged"
SelectedItem="{Binding SelectedItem, ElementName=UserControl}"
IsSwipeEnabled="false"
IsSynchronizedWithCurrentItem="False"
RequestedTheme="Dark"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<ListView.Resources>
<x:Double x:Key="HamburgerMenuSize">300</x:Double>
<DataTemplate x:Name="IsSpecial">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=48}" Margin="7,15,0,15">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding Path={Binding DisplayMemberPath, ElementName=UserControl}}" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock Text="{Binding Path={Binding DisplayMemberPath, ElementName=UserControl}}" x:Name="GroupTextBlock" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" FontStyle="Italic" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Name="IsNormal">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconId, Converter={StaticResource IntToSymbolConverter}, ConverterParameter=48}" Margin="7,15,0,15">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding Path={Binding DisplayMemberPath, ElementName=UserControl}}" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock Text="{Binding Path={Binding DisplayMemberPath, ElementName=UserControl}}" x:Name="GroupTextBlock" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" />
</StackPanel>
</DataTemplate>
</ListView.Resources>
<ListView.ItemTemplateSelector>
<templateSelectors:SelectableDataTemplateSelector FalseItem="{StaticResource IsNormal}" TrueItem="{StaticResource IsSpecial}" />
</ListView.ItemTemplateSelector>
<ListView.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ToggleButton Style="{StaticResource HamburgerToggleButton}">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding HeaderLabel, ElementName=UserControl}" />
</ToolTipService.ToolTip>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked">
<core:ChangePropertyAction PropertyName="Width" Value="{StaticResource HamburgerMenuSize}" TargetObject="{Binding ResizeTarget, ElementName=UserControl}"/>
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<core:ChangePropertyAction PropertyName="Width" Value="{StaticResource MenuSize}" TargetObject="{Binding ResizeTarget, ElementName=UserControl}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ToggleButton>
<TextBlock Text="{Binding HeaderLabel, ElementName=UserControl}" FontWeight="Bold" FontSize="18" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" HorizontalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.FooterTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Border BorderBrush="White" BorderThickness="0,0,0,1" />
<Button Padding="0" Height="{StaticResource MenuSize}" Margin="0" Visibility="{Binding IsButtonVisible, ElementName=UserControl}" Style="{StaticResource NoBorderButtonStyle}" Background="Transparent" BorderThickness="0" Width="{StaticResource HamburgerMenuSize}" HorizontalContentAlignment="Left" Click="ButtonBase_OnClick">
<StackPanel Orientation="Horizontal" Margin="17,0,5,0">
<SymbolIcon Symbol="Add">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding ButtonLabel, ElementName=UserControl}" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock Text="{Binding ButtonLabel, ElementName=UserControl}" FontWeight="SemiBold" TextWrapping="NoWrap" FontSize="16" VerticalAlignment="Center" Margin="30,0,20,0" />
</StackPanel>
</Button>
<Button Padding="0" Height="{StaticResource MenuSize}" Margin="0" Style="{StaticResource NoBorderButtonStyle}" Background="Transparent" BorderThickness="0" Width="{StaticResource HamburgerMenuSize}" HorizontalContentAlignment="Left">
<StackPanel Orientation="Horizontal" Margin="17,0,5,0">
<SymbolIcon Symbol="Home">
<ToolTipService.ToolTip>
<ToolTip x:Uid="HamburgerMenuHomeTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock x:Uid="HamburgerMenuHomeLabel" FontWeight="SemiBold" TextWrapping="NoWrap" FontSize="16" VerticalAlignment="Center" Margin="30,0,20,0" />
</StackPanel>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.MainPage10" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Button>
<Button Padding="0" Height="{StaticResource MenuSize}" Margin="0" Style="{StaticResource NoBorderButtonStyle}" Background="Transparent" BorderThickness="0" Width="{StaticResource HamburgerMenuSize}" HorizontalContentAlignment="Left">
<StackPanel Orientation="Horizontal" Margin="17,0,5,0">
<SymbolIcon Symbol="Setting">
<ToolTipService.ToolTip>
<ToolTip x:Uid="HamburgerMenuSettingsTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock x:Uid="HamburgerMenuSettingsLabel" FontWeight="SemiBold" TextWrapping="NoWrap" FontSize="16" VerticalAlignment="Center" Margin="30,0,20,0" />
</StackPanel>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.SettingsPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Button>
</StackPanel>
</DataTemplate>
</ListView.FooterTemplate>
</ListView>
</UserControl>

View File

@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Domain.Entities;
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
namespace ModernKeePass.Views.UserControls
{
public sealed partial class HamburgerMenuUserControl
{
public HamburgerMenuUserControl()
{
InitializeComponent();
}
public string HeaderLabel
{
get => (string)GetValue(HeaderLabelProperty);
set => SetValue(HeaderLabelProperty, value);
}
public static readonly DependencyProperty HeaderLabelProperty =
DependencyProperty.Register(
"HeaderLabel",
typeof(string),
typeof(HamburgerMenuUserControl),
new PropertyMetadata("Header", (o, args) => { }));
public string ButtonLabel
{
get => (string)GetValue(ButtonLabelProperty);
set => SetValue(ButtonLabelProperty, value);
}
public static readonly DependencyProperty ButtonLabelProperty =
DependencyProperty.Register(
"ButtonLabel",
typeof(string),
typeof(HamburgerMenuUserControl),
new PropertyMetadata("Button", (o, args) => { }));
public string DisplayMemberPath
{
get => (string)GetValue(DisplayMemberPathProperty);
set => SetValue(DisplayMemberPathProperty, value);
}
public static readonly DependencyProperty DisplayMemberPathProperty =
DependencyProperty.Register(
"DisplayMemberPath",
typeof(string),
typeof(HamburgerMenuUserControl),
new PropertyMetadata("Title", (o, args) => { }));
public object ResizeTarget
{
get => GetValue(ResizeTargetProperty);
set => SetValue(ResizeTargetProperty, value);
}
public static readonly DependencyProperty ResizeTargetProperty =
DependencyProperty.Register(
"ResizeTarget",
typeof(object),
typeof(HamburgerMenuUserControl),
new PropertyMetadata(null, (o, args) => { }));
public Visibility IsButtonVisible
{
get => (Visibility)GetValue(IsButtonVisibleProperty);
set => SetValue(IsButtonVisibleProperty, value);
}
public static readonly DependencyProperty IsButtonVisibleProperty =
DependencyProperty.Register(
"IsButtonVisible",
typeof(Visibility),
typeof(HamburgerMenuUserControl),
new PropertyMetadata(Visibility.Collapsed, (o, args) => { }));
public IEnumerable<Entity> ItemsSource
{
get => (IEnumerable<Entity>)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",
typeof(IEnumerable<Entity>),
typeof(HamburgerMenuUserControl),
new PropertyMetadata(new List<Entity>(), (o, args) => { }));
public object SelectedItem
{
get => GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
"SelectedItem",
typeof(object),
typeof(HamburgerMenuUserControl),
new PropertyMetadata(null, (o, args) => { }));
public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
SelectionChanged?.Invoke(sender, e);
}
public event EventHandler<RoutedEventArgs> ButtonClicked;
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
ButtonClicked?.Invoke(sender, e);
}
}
}

View File

@@ -0,0 +1,24 @@
<UserControl x:Name="UserControl"
x:Class="ModernKeePass.Views.UserControls.SymbolPickerUserControl"
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"
mc:Ignorable="d">
<ComboBox x:Name="ComboBox"
Height="35"
Width="Auto"
ItemsSource="{Binding Symbols, ElementName=UserControl}"
SelectedItem="{Binding SelectedSymbol, ElementName=UserControl, Mode=TwoWay}"
Loaded="ComboBox_OnLoaded"
ItemContainerStyle="{StaticResource MainColorComboBoxItem}"
Style="{StaticResource MainColorComboBox}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,10,0">
<SymbolIcon Symbol="{Binding}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</UserControl>

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Converters;
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
namespace ModernKeePass.Views.UserControls
{
public sealed partial class SymbolPickerUserControl
{
public IEnumerable<Symbol> Symbols { get; }
public Symbol SelectedSymbol
{
get => (Symbol)GetValue(SelectedSymbolProperty);
set => SetValue(SelectedSymbolProperty, value);
}
public static readonly DependencyProperty SelectedSymbolProperty =
DependencyProperty.Register(
"SelectedSymbol",
typeof(Symbol),
typeof(SymbolPickerUserControl),
new PropertyMetadata(Symbol.Stop, (o, args) => { }));
public SymbolPickerUserControl()
{
InitializeComponent();
var converter = new IconToSymbolConverter();
Symbols = Enum.GetValues(typeof(Symbol)).Cast<Symbol>().Where(s => (int)converter.ConvertBack(s, null, null, string.Empty) != -1);
}
private void ComboBox_OnLoaded(object sender, RoutedEventArgs e)
{
ComboBox.SelectedItem = Symbols.FirstOrDefault(s => s == SelectedSymbol);
}
}
}

View File

@@ -0,0 +1,101 @@
<UserControl x:Name="UserControl"
x:Class="ModernKeePass.Views.UserControls.TopMenuUserControl"
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"
mc:Ignorable="d">
<UserControl.Resources>
<Style BasedOn="{StaticResource NoBorderButtonStyle}" TargetType="Button" x:Key="MenuButtonStyle" >
<Setter Property="Padding" Value="25,0,25,0" />
<Setter Property="Background" Value="{ThemeResource ToggleButtonBackgroundThemeBrush}" />
<Setter Property="Height" Value="{StaticResource MenuSize}" />
</Style>
<Style BasedOn="{StaticResource NoBorderToggleButtonStyle}" TargetType="ToggleButton" x:Key="MenuToggleButtonStyle" >
<Setter Property="Padding" Value="25,0,25,0" />
<Setter Property="Height" Value="{StaticResource MenuSize}" />
</Style>
</UserControl.Resources>
<StackPanel Orientation="Horizontal">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisibilityStates">
<VisualState x:Name="Overflowed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowButtons" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MoreButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Collapsed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverflowButtons" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MoreButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel x:Name="OverflowButtons" Orientation="Horizontal">
<Button Command="{Binding RestoreCommand, ElementName=UserControl}" Visibility="{Binding RestoreButtonVisibility, ElementName=UserControl}" Click="RestoreButton_Click" Style="{StaticResource MenuButtonStyle}">
<SymbolIcon Symbol="Undo">
<ToolTipService.ToolTip>
<ToolTip x:Uid="TopMenuRestoreButton" />
</ToolTipService.ToolTip>
</SymbolIcon>
</Button>
<Button Command="{Binding SaveCommand, ElementName=UserControl}" Style="{StaticResource MenuButtonStyle}">
<SymbolIcon Symbol="Save">
<ToolTipService.ToolTip>
<ToolTip x:Uid="TopMenuSaveButton" />
</ToolTipService.ToolTip>
</SymbolIcon>
</Button>
<Button Visibility="{Binding SortButtonVisibility, ElementName=UserControl}" Style="{StaticResource MenuButtonStyle}">
<SymbolIcon Symbol="Sort">
<ToolTipService.ToolTip>
<ToolTip x:Uid="TopMenuSortButton" />
</ToolTipService.ToolTip>
</SymbolIcon>
<Button.Flyout>
<MenuFlyout Opening="SortFlyout_OnOpening">
<MenuFlyoutItem x:Uid="AppBarSortEntries" x:Name="SortEntriesButtonFlyout" Command="{Binding SortEntriesCommand, ElementName=UserControl}" />
<MenuFlyoutItem x:Uid="AppBarSortGroups" x:Name="SortGroupsButtonFlyout" Command="{Binding SortGroupsCommand, ElementName=UserControl}" />
</MenuFlyout>
</Button.Flyout>
</Button>
<ToggleButton 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" />
</ToolTipService.ToolTip>
</SymbolIcon>
</ToggleButton>
<Button Command="{Binding DeleteCommand, ElementName=UserControl}" IsEnabled="{Binding IsDeleteButtonEnabled, ElementName=UserControl}" Visibility="{Binding DeleteButtonVisibility, ElementName=UserControl}" Click="DeleteButton_Click" Style="{StaticResource MenuButtonStyle}">
<SymbolIcon Symbol="Delete">
<ToolTipService.ToolTip>
<ToolTip x:Uid="TopMenuDeleteButton" />
</ToolTipService.ToolTip>
</SymbolIcon>
</Button>
</StackPanel>
<Button x:Name="MoreButton" Style="{StaticResource MenuButtonStyle}">
<SymbolIcon Symbol="More" />
<Button.Flyout>
<MenuFlyout Opening="OverflowFlyout_OnOpening">
<MenuFlyoutItem x:Uid="TopMenuRestoreFlyout" x:Name="RestoreFlyout" Command="{Binding RestoreCommand, ElementName=UserControl}" Click="RestoreButton_Click" Visibility="{Binding RestoreButtonVisibility, ElementName=UserControl}" />
<MenuFlyoutItem x:Uid="TopMenuSaveFlyout" Command="{Binding SaveCommand, ElementName=UserControl}" />
<ToggleMenuFlyoutItem x:Uid="TopMenuEditFlyout" x:Name="EditFlyout" Command="{Binding EditCommand, ElementName=UserControl}" IsChecked="{Binding IsEditButtonChecked, ElementName=UserControl, Mode=TwoWay}" Click="EditButton_Click" />
<MenuFlyoutItem x:Uid="TopMenuDeleteFlyout" x:Name="DeleteFlyout" Command="{Binding DeleteCommand, ElementName=UserControl}" Click="DeleteButton_Click" Visibility="{Binding DeleteButtonVisibility, ElementName=UserControl}" IsEnabled="{Binding IsDeleteButtonEnabled, ElementName=UserControl}" />
<MenuFlyoutItem x:Uid="TopMenuSortEntriesFlyout" x:Name="SortEntriesFlyout" Command="{Binding SortEntriesCommand, ElementName=UserControl}" Visibility="{Binding SortButtonVisibility, ElementName=UserControl}" />
<MenuFlyoutItem x:Uid="TopMenuSortGroupsFlyout" x:Name="SortGroupsFlyout" Command="{Binding SortGroupsCommand, ElementName=UserControl}" Visibility="{Binding SortButtonVisibility, ElementName=UserControl}" />
</MenuFlyout>
</Button.Flyout>
</Button>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,195 @@
using System;
using System.Windows.Input;
using Windows.UI.Xaml;
// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
namespace ModernKeePass.Views.UserControls
{
public sealed partial class TopMenuUserControl
{
public ICommand SaveCommand
{
get => (ICommand)GetValue(SaveCommandProperty);
set => SetValue(SaveCommandProperty, value);
}
public static readonly DependencyProperty SaveCommandProperty =
DependencyProperty.Register(
"SaveCommand",
typeof(ICommand),
typeof(TopMenuUserControl),
new PropertyMetadata(null, (o, args) => { }));
public ICommand EditCommand
{
get => (ICommand)GetValue(EditCommandProperty);
set => SetValue(EditCommandProperty, value);
}
public static readonly DependencyProperty EditCommandProperty =
DependencyProperty.Register(
"EditCommand",
typeof(ICommand),
typeof(TopMenuUserControl),
new PropertyMetadata(null, (o, args) => { }));
public ICommand DeleteCommand
{
get => (ICommand)GetValue(DeleteCommandProperty);
set => SetValue(DeleteCommandProperty, value);
}
public static readonly DependencyProperty DeleteCommandProperty =
DependencyProperty.Register(
"DeleteCommand",
typeof(ICommand),
typeof(TopMenuUserControl),
new PropertyMetadata(null, (o, args) => { }));
public ICommand RestoreCommand
{
get => (ICommand)GetValue(RestoreCommandProperty);
set => SetValue(RestoreCommandProperty, value);
}
public static readonly DependencyProperty RestoreCommandProperty =
DependencyProperty.Register(
"RestoreCommand",
typeof(ICommand),
typeof(TopMenuUserControl),
new PropertyMetadata(null, (o, args) => { }));
public ICommand SortEntriesCommand
{
get => (ICommand)GetValue(SortEntriesCommandProperty);
set => SetValue(SortEntriesCommandProperty, value);
}
public static readonly DependencyProperty SortEntriesCommandProperty =
DependencyProperty.Register(
"SortEntriesCommand",
typeof(ICommand),
typeof(TopMenuUserControl),
new PropertyMetadata(null, (o, args) => { }));
public ICommand SortGroupsCommand
{
get => (ICommand)GetValue(SortGroupsCommandProperty);
set => SetValue(SortGroupsCommandProperty, value);
}
public static readonly DependencyProperty SortGroupsCommandProperty =
DependencyProperty.Register(
"SortGroupsCommand",
typeof(ICommand),
typeof(TopMenuUserControl),
new PropertyMetadata(null, (o, args) => { }));
public Visibility RestoreButtonVisibility
{
get => (Visibility)GetValue(RestoreButtonVisibilityProperty);
set => SetValue(RestoreButtonVisibilityProperty, value);
}
public static readonly DependencyProperty RestoreButtonVisibilityProperty =
DependencyProperty.Register(
"RestoreButtonVisibility",
typeof(Visibility),
typeof(TopMenuUserControl),
new PropertyMetadata(Visibility.Collapsed, (o, args) => { }));
public Visibility DeleteButtonVisibility
{
get => (Visibility)GetValue(DeleteButtonVisibilityProperty);
set => SetValue(DeleteButtonVisibilityProperty, value);
}
public static readonly DependencyProperty DeleteButtonVisibilityProperty =
DependencyProperty.Register(
"DeleteButtonVisibility",
typeof(Visibility),
typeof(TopMenuUserControl),
new PropertyMetadata(Visibility.Collapsed, (o, args) => { }));
public Visibility SortButtonVisibility
{
get => (Visibility)GetValue(SortButtonVisibilityProperty);
set => SetValue(SortButtonVisibilityProperty, value);
}
public static readonly DependencyProperty SortButtonVisibilityProperty =
DependencyProperty.Register(
"SortButtonVisibility",
typeof(Visibility),
typeof(TopMenuUserControl),
new PropertyMetadata(Visibility.Collapsed, (o, args) => { }));
public bool IsDeleteButtonEnabled
{
get => (bool)GetValue(IsDeleteButtonEnabledProperty);
set => SetValue(IsDeleteButtonEnabledProperty, value);
}
public static readonly DependencyProperty IsDeleteButtonEnabledProperty =
DependencyProperty.Register(
"IsDeleteButtonEnabled",
typeof(bool),
typeof(TopMenuUserControl),
new PropertyMetadata(true, (o, args) => { }));
public bool IsEditButtonChecked
{
get => (bool)GetValue(IsEditButtonCheckedProperty);
set => SetValue(IsEditButtonCheckedProperty, value);
}
public static readonly DependencyProperty IsEditButtonCheckedProperty =
DependencyProperty.Register(
"IsEditButtonChecked",
typeof(bool),
typeof(TopMenuUserControl),
new PropertyMetadata(false, (o, args) => { }));
public event EventHandler<RoutedEventArgs> EditButtonClick;
public event EventHandler<RoutedEventArgs> DeleteButtonClick;
public event EventHandler<RoutedEventArgs> RestoreButtonClick;
public TopMenuUserControl()
{
InitializeComponent();
EditFlyout.Click += EditFlyout_Click;
}
private void EditFlyout_Click(object sender, RoutedEventArgs e)
{
IsEditButtonChecked = EditFlyout.IsChecked;
EditButton_Click(sender, e);
}
private void EditButton_Click(object sender, RoutedEventArgs e)
{
EditButtonClick?.Invoke(sender, e);
}
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
DeleteButtonClick?.Invoke(sender, e);
}
private void RestoreButton_Click(object sender, RoutedEventArgs e)
{
RestoreButtonClick?.Invoke(sender, e);
}
private void OverflowFlyout_OnOpening(object sender, object e)
{
DeleteFlyout.IsEnabled = IsDeleteButtonEnabled;
DeleteFlyout.Visibility = DeleteButtonVisibility;
EditFlyout.IsChecked = IsEditButtonChecked;
RestoreFlyout.Visibility = RestoreButtonVisibility;
SortEntriesFlyout.Visibility = SortButtonVisibility;
SortGroupsFlyout.Visibility = SortButtonVisibility;
SortEntriesFlyout.Command = SortEntriesCommand;
SortGroupsFlyout.Command = SortGroupsCommand;
}
private void SortFlyout_OnOpening(object sender, object e)
{
SortEntriesButtonFlyout.Command = SortEntriesCommand;
SortGroupsButtonFlyout.Command = SortGroupsCommand;
}
}
}

View File

@@ -0,0 +1,72 @@
<UserControl
x:Class="ModernKeePass.Views.UserControls.UpdateCredentialsUserControl"
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:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:viewModels="using:ModernKeePass.ViewModels"
mc:Ignorable="d" >
<UserControl.Resources>
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter"/>
<converters:DoubleToSolidColorBrushConverter x:Key="DoubleToSolidColorBrushConverter"/>
<converters:DiscreteIntToSolidColorBrushConverter x:Key="DiscreteIntToSolidColorBrushConverter"/>
<converters:EmptyStringToVisibilityConverter x:Key="EmptyStringToVisibilityConverter"/>
</UserControl.Resources>
<Grid x:Name="Grid">
<!-- DataContext is not set at the root of the control because of issues happening when displaying it -->
<Grid.DataContext>
<viewModels:UpdateCredentialsViewModel />
</Grid.DataContext>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="45" />
<RowDefinition Height="40" />
<RowDefinition Height="40" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" IsChecked="{x:Bind ViewModel.HasPassword, Mode=TwoWay}" />
<PasswordBox Grid.Row="0" Grid.Column="1" x:Uid="CompositeKeyPassword" Password="{x:Bind ViewModel.Password, Mode=TwoWay}" Height="30" PasswordRevealMode="Peek" BorderBrush="{x:Bind ViewModel.StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" SelectionHighlightColor="{StaticResource MainColor}" >
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="GotFocus">
<core:ChangePropertyAction TargetObject="{Binding}" PropertyName="HasPassword" Value="True" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</PasswordBox>
<PasswordBox Grid.Row="0" Grid.Column="1" x:Uid="CompositeKeyPassword" Password="{x:Bind ViewModel.Password, Mode=TwoWay}" Height="30" PasswordRevealMode="Peek" BorderBrush="{x:Bind ViewModel.StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" SelectionHighlightColor="{StaticResource MainColor}" />
<ProgressBar Grid.Row="0" Grid.Column="1"
Maximum="128" VerticalAlignment="Bottom"
Value="{x:Bind ViewModel.PasswordComplexityIndicator, ConverterParameter=0\,128, Converter={StaticResource ProgressBarLegalValuesConverter}, Mode=OneWay}"
Foreground="{x:Bind ViewModel.PasswordComplexityIndicator, ConverterParameter=128, Converter={StaticResource DoubleToSolidColorBrushConverter}, Mode=OneWay}"/>
<CheckBox Grid.Row="1" Grid.Column="0" IsChecked="{x:Bind ViewModel.HasKeyFile, Mode=TwoWay}" />
<HyperlinkButton Grid.Row="1" Grid.Column="1" Margin="-15,0,0,0"
Content="{x:Bind ViewModel.KeyFileText, Mode=OneWay}"
IsEnabled="{x:Bind ViewModel.HasKeyFile, Mode=OneWay}"
Click="KeyFileButton_Click"
Style="{StaticResource MainColorHyperlinkButton}" />
<HyperlinkButton Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right"
IsEnabled="{x:Bind ViewModel.HasKeyFile, Mode=OneWay}"
Style="{StaticResource MainColorHyperlinkButton}"
Click="CreateKeyFileButton_Click">
<SymbolIcon Symbol="Add">
<ToolTipService.ToolTip>
<ToolTip x:Uid="CompositeKeyNewKeyFileTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
</HyperlinkButton>
<Button Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2"
x:Uid="UpdateCredentialsOkButton"
Click="UpdateButton_OnClick"
Style="{StaticResource MainColorButton}"
IsEnabled="{x:Bind ViewModel.IsValid, Mode=OneWay}" />
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="3" Height="Auto" FontSize="14" FontWeight="Light"
Text="{x:Bind ViewModel.Status, Mode=OneWay}"
Foreground="{x:Bind ViewModel.StatusType, Mode=OneWay, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}"
Visibility="{x:Bind ViewModel.Status, Mode=OneWay, Converter={StaticResource EmptyStringToVisibilityConverter}}" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using Windows.Storage.AccessCache;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
using ModernKeePass.Domain.Dtos;
using ModernKeePass.ViewModels;
using ModernKeePass.Extensions;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace ModernKeePass.Views.UserControls
{
public sealed partial class UpdateCredentialsUserControl
{
public UpdateCredentialsViewModel ViewModel => Grid.DataContext as UpdateCredentialsViewModel;
public string DatabaseFilePath
{
get => (string)GetValue(DatabaseFilePathProperty);
set => SetValue(DatabaseFilePathProperty, value);
}
public static readonly DependencyProperty DatabaseFilePathProperty =
DependencyProperty.Register(
"DatabaseFilePath",
typeof(string),
typeof(CredentialsUserControl),
new PropertyMetadata(null, (o, args) => { }));
public event EventHandler CredentialsUpdated;
public UpdateCredentialsUserControl()
{
InitializeComponent();
}
private async void KeyFileButton_Click(object sender, RoutedEventArgs e)
{
var picker =
new FileOpenPicker
{
ViewMode = PickerViewMode.List,
SuggestedStartLocation = PickerLocationId.DocumentsLibrary
};
picker.FileTypeFilter.Add(".key");
// Application now has read/write access to the picked file
var file = await picker.PickSingleFileAsync();
if (file == null) return;
var token = StorageApplicationPermissions.FutureAccessList.Add(file);
ViewModel.KeyFilePath = token;
}
private async void CreateKeyFileButton_Click(object sender, RoutedEventArgs e)
{
var savePicker = new FileSavePicker
{
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
SuggestedFileName = "Key"
};
savePicker.FileTypeChoices.Add("Key file", new List<string> { ".key" });
var file = await savePicker.PickSaveFileAsync();
if (file == null) return;
var token = StorageApplicationPermissions.FutureAccessList.Add(file);
ViewModel.KeyFilePath = token;
}
private async void UpdateButton_OnClick(object sender, RoutedEventArgs e)
{
//throw new NotImplementedException();
if (await Dispatcher.RunTaskAsync(async () =>
{
var fileInfo = new FileInfo
{
Path = DatabaseFilePath
};
return await ViewModel.CreateDatabase(fileInfo);
}))
{
CredentialsUpdated?.Invoke(this, new EventArgs());
}
}
}
}