Implemented Donate page with wirking (?) in app purchases

Moved Pages to Views
This commit is contained in:
BONNEVILLE Geoffroy
2017-12-08 19:38:33 +01:00
parent e25f9f4aae
commit 35f64eec1b
48 changed files with 402 additions and 227 deletions

View File

@@ -0,0 +1,208 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.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 virtual 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)
{
// TODO: Assign a bindable group to Me.DefaultViewModel("Group")
// TODO: Assign a collection of bindable items to Me.DefaultViewModel("Items")
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,515 @@
<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"
x:Name="PageRoot"
x:Class="ModernKeePass.Views.EntryDetailPage"
mc:Ignorable="d">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter"/>
<converters:DoubleToSolidColorBrushConverter x:Key="DoubleToForegroungBrushComplexityConverter"/>
<converters:NullToBooleanConverter x:Key="NullToBooleanConverter"/>
<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="{ThemeResource TextSelectionHighlightColorThemeBrush}" />
<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="{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="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"
Grid.RowSpan="1"/>
<Border x:Name="BorderElement"
Grid.Row="1"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Grid.ColumnSpan="3"
Grid.RowSpan="1"/>
<ContentPresenter x:Name="HeaderContentPresenter"
Grid.Row="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"
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"
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" />
<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">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction TargetObject="{Binding}" MethodName="GeneratePassword"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Button>
</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>
<Page.BottomAppBar>
<CommandBar x:Name="CommandBar" VerticalAlignment="Center">
<CommandBar.SecondaryCommands>
<AppBarButton Icon="Home" x:Uid="AppBarHome">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.MainPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Save" x:Uid="AppBarSave">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction TargetObject="{Binding}" MethodName="Save"/>
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Setting" x:Uid="AppBarSettings">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.SettingsPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
</CommandBar.SecondaryCommands>
<AppBarToggleButton Icon="Edit" x:Uid="AppBarEdit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarToggleButton>
<AppBarButton Icon="Undo" x:Uid="AppBarRestore" Visibility="{Binding ParentGroup.IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding PreviousGroup, Converter={StaticResource NullToBooleanConverter}}" Click="RestoreButton_Click">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction MethodName="UndoDelete" TargetObject="{Binding}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Delete" x:Uid="AppBarDelete" Click="DeleteButton_Click" />
</CommandBar>
</Page.BottomAppBar>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="stackPanel" Grid.Row="1" Margin="20,0,0,0" >
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="0,20,0,0"/>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Margin" Value="0,20,0,0"/>
</Style>
</StackPanel.Resources>
<TextBlock x:Uid="EntryLogin" TextWrapping="Wrap" FontSize="18"/>
<local:TextBoxWithButton HorizontalAlignment="Left" Text="{Binding UserName, Mode=TwoWay}" Width="350" Height="32" Style="{StaticResource TextBoxWithButtonStyle}" ButtonSymbol="&#xE16F;" ButtonTooltip="Copy">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{Binding UserName}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</local:TextBoxWithButton>
<TextBlock x:Uid="EntryPassword" TextWrapping="Wrap" FontSize="18"/>
<PasswordBox HorizontalAlignment="Left" Password="{Binding Password, Mode=TwoWay}" Width="350" Height="32" IsPasswordRevealButtonEnabled="True" Visibility="{Binding IsRevealPassword, Converter={StaticResource InverseBooleanToVisibilityConverter}}" Style="{StaticResource PasswordBoxWithButtonStyle}" />
<local:TextBoxWithButton HorizontalAlignment="Left" Text="{Binding Password, Mode=TwoWay}" Width="350" Height="32" Visibility="{Binding IsRevealPassword, Converter={StaticResource BooleanToVisibilityConverter}}" Style="{StaticResource TextBoxWithButtonStyle}" ButtonSymbol="&#xE16F;" ButtonTooltip="Copy">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ButtonClick">
<actions:ClipboardAction Text="{Binding Password}" />
</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:Name="UrlTextBox" HorizontalAlignment="Left" Text="{Binding Url, Mode=TwoWay}" Height="32" Width="350" MaxLength="256" Style="{StaticResource TextBoxWithButtonStyle}" ButtonClick="UrlButton_Click" ButtonSymbol="&#xE111;" ButtonTooltip="Navigate to URL" />
<TextBlock x:Uid="EntryNotes" TextWrapping="Wrap" FontSize="18"/>
<TextBox HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Notes, Mode=TwoWay}" Width="350" Height="200" AcceptsReturn="True" IsSpellCheckEnabled="True" />
<CheckBox x:Uid="EntryExpirationDate" FontSize="18" IsChecked="{Binding HasExpirationDate, Mode=TwoWay}" />
<StackPanel Orientation="Horizontal" Visibility="{Binding HasExpirationDate, Converter={StaticResource BooleanToVisibilityConverter}}">
<SymbolIcon Symbol="Important" Foreground="DarkRed" Visibility="{Binding HasExpired, Converter={StaticResource BooleanToVisibilityConverter}}">
<ToolTipService.ToolTip>
<ToolTip x:Uid="EntryExpirationTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<DatePicker Margin="0,0,20,0" Date="{Binding ExpiryDate, Mode=TwoWay}" />
<TimePicker Time="{Binding ExpiryTime, Mode=TwoWay}"/>
</StackPanel>
</StackPanel>
<!-- Bouton Précédent et titre de la page -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
x:Name="BackButton"
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Height="50"
Width="50"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<StackPanel Grid.Column="1" >
<TextBox
x:Name="TitleTextBox"
Text="{Binding Name, Mode=TwoWay}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
Background="Transparent"
IsHitTestVisible="{Binding IsEditMode}"
BorderThickness="0"
FontSize="20"
FontWeight="SemiBold"
TextWrapping="NoWrap"
VerticalAlignment="Center"
PlaceholderText="New entry name...">
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsEditMode}" Value="True">
<actions:SetupFocusAction TargetObject="{Binding ElementName=TitleTextBox}" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
<TextBlock FontSize="12" Text="{Binding Path}" />
</StackPanel>
</Grid>
</Grid>
</Page>

View File

@@ -0,0 +1,109 @@
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.Services;
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);
NavigationHelper.LoadState += navigationHelper_LoadState;
}
/// <summary>
/// Remplit la page à l'aide du contenu passé lors de la navigation. Tout état enregistré est également
/// fourni lorsqu'une page est recréée à partir d'une session antérieure.
/// </summary>
/// <param name="sender">
/// Source de l'événement ; en général <see cref="Common.NavigationHelper"/>
/// </param>
/// <param name="e">Données d'événement qui fournissent le paramètre de navigation transmis à
/// <see cref="Frame.Navigate(Type, object)"/> lors de la requête initiale de cette page et
/// un dictionnaire d'état conservé par cette page durant une session
/// antérieure. L'état n'aura pas la valeur Null lors de la première visite de la page.</param>
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e) {}
#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);
}
#endregion
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
var resource = new ResourcesService();
var message = Model.IsRecycleOnDelete
? resource.GetResourceValue("EntryRecyclingConfirmation")
: resource.GetResourceValue("EntryDeletingConfirmation");
var text = Model.IsRecycleOnDelete ? resource.GetResourceValue("EntryRecycled") : resource.GetResourceValue("EntryDeleted");
MessageDialogHelper.ShowActionDialog(resource.GetResourceValue("EntityDeleteTitle"), message,
resource.GetResourceValue("EntityDeleteActionButton"),
resource.GetResourceValue("EntityDeleteCancelButton"), a =>
{
ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityDeleting"), text);
Model.MarkForDelete();
if (Frame.CanGoBack) Frame.GoBack();
});
}
private void RestoreButton_Click(object sender, RoutedEventArgs e)
{
var resource = new ResourcesService();
ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityRestoredTitle"), resource.GetResourceValue("EntryRestored"));
if (Frame.CanGoBack) Frame.GoBack();
}
private async void UrlButton_Click(object sender, RoutedEventArgs e)
{
try
{
var uri = new Uri(UrlTextBox.Text);
await Windows.System.Launcher.LaunchUriAsync(uri);
}
catch (Exception ex)
{
MessageDialogHelper.ShowErrorDialog(ex);
}
}
}
}

View File

@@ -0,0 +1,360 @@
<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:controls="using:ModernKeePass.Controls"
xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors"
x:Name="PageRoot"
x:Class="ModernKeePass.Views.GroupDetailPage"
mc:Ignorable="d" >
<Page.Resources>
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<converters:BooleanToFontStyleConverter x:Key="BooleanToFontStyleConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter"/>
<converters:NullToBooleanConverter x:Key="NullToBooleanConverter"/>
</Page.Resources>
<Page.DataContext>
<viewModels:GroupVm />
</Page.DataContext>
<Page.BottomAppBar>
<CommandBar x:Name="CommandBar" VerticalAlignment="Center">
<CommandBar.SecondaryCommands>
<AppBarButton Icon="Home" x:Uid="AppBarHome">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.MainPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Save" x:Uid="AppBarSave">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction TargetObject="{Binding}" MethodName="Save"/>
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Setting" x:Uid="AppBarSettings">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.SettingsPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
</CommandBar.SecondaryCommands>
<AppBarButton Icon="Sort" x:Uid="AppBarSort" Visibility="{Binding IsEditMode, Converter={StaticResource BooleanToVisibilityConverter}}">
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="AppBarSortEntries">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction MethodName="SortEntries" TargetObject="{Binding}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
<MenuFlyoutItem x:Uid="AppBarSortGroups">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction MethodName="SortGroups" TargetObject="{Binding}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</MenuFlyoutItem>
</MenuFlyout>
</Button.Flyout>
</AppBarButton>
<AppBarToggleButton Icon="Edit" x:Uid="AppBarEdit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:ChangePropertyAction TargetObject="{Binding ElementName=CommandBar}" PropertyName="IsOpen" Value="False" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarToggleButton>
<AppBarButton Icon="Undo" x:Uid="AppBarRestore" Visibility="{Binding ShowRestore, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding PreviousGroup, Converter={StaticResource NullToBooleanConverter}}" Click="RestoreButton_Click">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction MethodName="UndoDelete" TargetObject="{Binding}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<AppBarButton Icon="Delete" x:Uid="AppBarDelete" IsEnabled="{Binding IsNotRoot}" Click="DeleteButton_Click" />
</CommandBar>
</Page.BottomAppBar>
<Grid>
<Grid.Resources>
<CollectionViewSource
x:Name="GroupsViewSource"
Source="{Binding Groups}" />
<CollectionViewSource
x:Name="EntriesViewSource"
Source="{Binding EntriesFiltered}" />
<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="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" x:Name="LeftListViewColumn" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListView
Grid.Column="0"
x:Name="LeftListView"
Margin="0,0,0,-10"
SelectionChanged="groups_SelectionChanged"
IsSwipeEnabled="false"
IsSynchronizedWithCurrentItem="False"
DataContext="{Binding DataContext, ElementName=PageRoot}"
RequestedTheme="Dark"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}">
<ListView.Resources>
<DataTemplate x:Name="GroupOtherItem">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0">
<ToolTipService.ToolTip>
<ToolTip Content="{Binding Name}" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock Text="{Binding Name}" x:Name="GroupTextBlock" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" FontStyle="{Binding IsSelected, Converter={StaticResource BooleanToFontStyleConverter}}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Name="GroupFirstItem">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0">
<ToolTipService.ToolTip>
<ToolTip x:Uid="GroupNewItemTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
<TextBlock x:Name="GroupTextBlock" x:Uid="GroupNewItemTextBox" FontWeight="SemiBold" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" />
</StackPanel>
</DataTemplate>
</ListView.Resources>
<ListView.ItemsSource>
<Binding Source="{StaticResource GroupsViewSource}"/>
</ListView.ItemsSource>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="AllowDrop" Value="True"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.HeaderTemplate>
<DataTemplate>
<ToggleButton Style="{StaticResource HamburgerToggleButton}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked">
<core:ChangePropertyAction PropertyName="Width" Value="Auto" TargetObject="{Binding ElementName=LeftListViewColumn}"/>
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<core:ChangePropertyAction PropertyName="Width" Value="50" TargetObject="{Binding ElementName=LeftListViewColumn}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</ToggleButton>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplateSelector>
<templateSelectors:FirstItemDataTemplateSelector
FirstItem="{StaticResource GroupFirstItem}"
OtherItem="{StaticResource GroupOtherItem}" />
</ListView.ItemTemplateSelector>
</ListView>
<!-- Horizontal scrolling grid -->
<HyperlinkButton Grid.Column="1" VerticalAlignment="Top" Margin="40,0,0,0" Click="CreateEntry_ButtonClick" Visibility="{Binding IsSelected, Converter={StaticResource InverseBooleanToVisibilityConverter}}" >
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Add"></SymbolIcon>
<TextBlock x:Uid="GroupCreateEntry" VerticalAlignment="Center" Margin="10,0,0,0" />
</StackPanel>
</HyperlinkButton>
<SemanticZoom Grid.Column="1" ViewChangeStarted="SemanticZoom_ViewChangeStarted" Margin="0,45,0,0">
<SemanticZoom.ZoomedInView>
<GridView
x:Name="GridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Entries"
TabIndex="1"
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" Background="{Binding BackgroundColor, ConverterParameter={StaticResource AppBarBackgroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<SymbolIcon Grid.Column="0" Symbol="{Binding IconSymbol}" Width="100" Height="100" RenderTransformOrigin="0.5,0.5" >
<SymbolIcon.RenderTransform>
<CompositeTransform ScaleX="2" TranslateX="0" TranslateY="0" ScaleY="2"/>
</SymbolIcon.RenderTransform>
</SymbolIcon>
<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}" MaxHeight="60" />
<TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsSource>
<Binding Source="{StaticResource EntriesViewSource}"/>
</GridView.ItemsSource>
<GridView.DataContext>
<viewModels:EntryVm/>
</GridView.DataContext>
<GridView.ItemContainerStyle>
<Style TargetType = "GridViewItem" >
<Setter Property = "Template" >
<Setter.Value>
<ControlTemplate TargetType = "GridViewItem" >
<GridViewItemPresenter
SelectionCheckMarkVisualEnabled="True"
CheckHintBrush="{ThemeResource ListViewItemCheckHintThemeBrush}"
CheckSelectingBrush="{ThemeResource ListViewItemCheckSelectingThemeBrush}"
CheckBrush="{ThemeResource ListViewItemCheckThemeBrush}"
DragBackground="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}"
DragForeground="{ThemeResource ListViewItemDragForegroundThemeBrush}"
FocusBorderBrush="{ThemeResource ListViewItemFocusBorderThemeBrush}"
PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}"
PointerOverBackground="{ThemeResource ListViewItemPointerOverBackgroundThemeBrush}"
SelectedBorderThickness="{ThemeResource GridViewItemCompactSelectedBorderThemeThickness}"
SelectedBackground="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}"
SelectedForeground="{ThemeResource ListViewItemSelectedForegroundThemeBrush}"
SelectedPointerOverBackground="{ThemeResource ListViewItemSelectedPointerOverBackgroundThemeBrush}"
SelectedPointerOverBorderBrush="{ThemeResource ListViewItemSelectedPointerOverBorderThemeBrush}"
DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
PointerOverBackgroundMargin="1"
ContentMargin="4" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GridView.ItemContainerStyle>
</GridView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<ListView
ItemsSource="{Binding Source={StaticResource EntriesZoomedOutViewSource}}"
IsSwipeEnabled="false"
SelectionChanged="groups_SelectionChanged"
IsSynchronizedWithCurrentItem="False">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="LightGray" Margin="0,0,10,0" HorizontalAlignment="Left">
<TextBlock Text="{Binding Key}" Width="40" Foreground="Black" Margin="30" Style="{StaticResource HeaderTextBlockStyle}"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</Grid>
<!-- Back button and page title -->
<Grid Grid.Row="0" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="400"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Height="50"
Width="50"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<StackPanel Grid.Column="1" >
<TextBox
x:Name="TitleTextBox"
Text="{Binding Name, Mode=TwoWay}"
Foreground="{ThemeResource DefaultTextForegroundThemeBrush}"
Background="Transparent"
IsHitTestVisible="{Binding IsEditMode}"
BorderThickness="0"
FontSize="20"
FontWeight="SemiBold"
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>
<TextBlock FontSize="12" Text="{Binding Path}" />
</StackPanel>
<controls:TextBoxWithButton Grid.Column="2" x:Uid="GroupSearch" ButtonSymbol="&#xE11A;" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="18" VerticalContentAlignment="Center" Style="{StaticResource TextBoxWithButtonStyle}" />
</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>
</VisualStateManager.VisualStateGroups>
</Grid>
</Page>

View File

@@ -0,0 +1,156 @@
using System;
using Windows.ApplicationModel.DataTransfer;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.Events;
using ModernKeePass.Services;
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);
NavigationHelper.LoadState += navigationHelper_LoadState;
}
/// <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>
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e) {}
#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)
DataContext = ((PasswordEventArgs) e.Parameter).RootGroup;
else if (e.Parameter is GroupVm)
DataContext = (GroupVm) e.Parameter;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
NavigationHelper.OnNavigatedFrom(e);
}
#endregion
#region Event Handlers
private void groups_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
GroupVm group;
switch (LeftListView.SelectedIndex)
{
case -1:
return;
case 0:
group = Model.AddNewGroup();
break;
default:
group = LeftListView.SelectedItem as GroupVm;
break;
}
Frame.Navigate(typeof(GroupDetailPage), group);
}
private void entries_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
EntryVm entry;
switch (GridView.SelectedIndex)
{
case -1:
return;
default:
entry = GridView.SelectedItem as EntryVm;
break;
}
Frame.Navigate(typeof(EntryDetailPage), entry);
}
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
var resource = new ResourcesService();
var message = Model.IsRecycleOnDelete
? resource.GetResourceValue("GroupRecyclingConfirmation")
: resource.GetResourceValue("GroupDeletingConfirmation");
var text = Model.IsRecycleOnDelete ? resource.GetResourceValue("GroupRecycled") : resource.GetResourceValue("GroupDeleted");
MessageDialogHelper.ShowActionDialog(resource.GetResourceValue("EntityDeleteTitle"), message,
resource.GetResourceValue("EntityDeleteActionButton"),
resource.GetResourceValue("EntityDeleteCancelButton"), a =>
{
ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityDeleting"), text);
Model.MarkForDelete();
if (Frame.CanGoBack) Frame.GoBack();
});
}
private void RestoreButton_Click(object sender, RoutedEventArgs e)
{
var resource = new ResourcesService();
ToastNotificationHelper.ShowMovedToast(Model, resource.GetResourceValue("EntityRestoredTitle"),
resource.GetResourceValue("GroupRestored"));
if (Frame.CanGoBack) Frame.GoBack();
}
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 == false)
{
e.DestinationItem.Item = e.SourceItem.Item;
}
}
#endregion
private void CreateEntry_ButtonClick(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(EntryDetailPage), Model.AddNewEntry());
}
private void GridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
e.Cancel = !Model.IsEditMode;
e.Data.RequestedOperation = DataPackageOperation.Move;
}
}
}

View File

@@ -0,0 +1,149 @@
<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="40"/>
<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="40"
VerticalAlignment="Center"
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,37 @@
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 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);
var selectedItem = Model.SelectedItem as MainMenuItemVm;
if (selectedItem == null) MenuFrame.Navigate(typeof(WelcomePage));
else selectedItem.Destination.Navigate(selectedItem.PageType, selectedItem.Parameter);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
DataContext = new MainVm(Frame, MenuFrame);
}
}
}

View File

@@ -0,0 +1,38 @@
<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:AboutVm/>
</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://github.com/wismna/ModernKeePass">
<Run Text="https://github.com/wismna/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,30 @@
<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:viewModels="using:ModernKeePass.ViewModels"
xmlns:converters="using:ModernKeePass.Converters"
mc:Ignorable="d">
<Page.DataContext>
<viewModels:DonateVm />
</Page.DataContext>
<Page.Resources>
<converters:NullToBooleanConverter x:Key="NullToBooleanConverter"/>
<CollectionViewSource
x:Name="DonateItemsSource"
Source="{Binding Donations}" />
</Page.Resources>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Uid="DonateDesc" Style="{StaticResource BodyTextBlockStyle}" />
<ItemsControl ItemsSource="{Binding Source={StaticResource DonateItemsSource}}" Margin="0,10,0,10">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton GroupName="DonateOptions" Content="{Binding FormattedPrice}" Checked="ToggleButton_OnChecked" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button x:Uid="DonateButton" Click="ButtonBase_OnClick" IsEnabled="{Binding SelectedItem, Converter={StaticResource NullToBooleanConverter}}" />
</StackPanel>
</Page>

View File

@@ -0,0 +1,71 @@
using System;
using Windows.ApplicationModel.Store;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
using ModernKeePass.Services;
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 DonatePage
{
public DonateVm Model => DataContext as DonateVm;
public DonatePage()
{
InitializeComponent();
}
private void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
{
var source = sender as RadioButton;
Model.SelectedItem = source?.DataContext as ProductListing;
}
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var resource = new ResourcesService();
try
{
var result = await Model.Purchase();
switch ((LicenseService.PurchaseResult)result)
{
case LicenseService.PurchaseResult.Succeeded:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonateSucceededTitle"), resource.GetResourceValue("DonateSucceededMessage"));
break;
case LicenseService.PurchaseResult.NothingToFulfill:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonateNothingToFulfillTitle"), resource.GetResourceValue("DonateNothingToFulfillMessage"));
break;
case LicenseService.PurchaseResult.PurchasePending:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonatePurchasePendingTitle"), resource.GetResourceValue("DonatePurchasePendingMessage"));
break;
case LicenseService.PurchaseResult.PurchaseReverted:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonatePurchaseRevertedTitle"), resource.GetResourceValue("DonatePurchaseRevertedMessage"));
break;
case LicenseService.PurchaseResult.ServerError:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonateServerErrorTitle"), resource.GetResourceValue("DonateServerErrorMessage"));
break;
case LicenseService.PurchaseResult.NotPurchased:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonateNotPurchasedTitle"), resource.GetResourceValue("DonateNotPurchasedMessage"));
break;
// Should never happen because these are consumables
case LicenseService.PurchaseResult.AlreadyPurchased:
MessageDialogHelper.ShowNotificationDialog(resource.GetResourceValue("DonateAlreadyPurchasedTitle"), resource.GetResourceValue("DonateAlreadyPurchasedMessage"));
break;
default:
throw new ArgumentOutOfRangeException();
}
}
catch (Exception exception)
{
MessageDialogHelper.ShowErrorDialog(exception);
}
}
}
}

View File

@@ -0,0 +1,40 @@
<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:local="using:ModernKeePass.Controls"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:userControls="using:ModernKeePass.Views.UserControls"
mc:Ignorable="d">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Page.Resources>
<Page.DataContext>
<viewModels:NewVm />
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<HyperlinkButton x:Uid="NewCreateButton" Click="ButtonBase_OnClick" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" x:Uid="NewCreateDesc" />
<Border HorizontalAlignment="Left" BorderThickness="1" BorderBrush="AliceBlue" Width="550" Visibility="{Binding ShowPasswordBox, Converter={StaticResource BooleanToVisibilityConverter}}">
<StackPanel Margin="25,0,25,0">
<TextBlock Text="{Binding Name}" />
<userControls:CompositeKeyUserControl CreateNew="True" ButtonLabel="Save" >
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ValidationChecked">
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.GroupDetailPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</userControls:CompositeKeyUserControl>
</StackPanel>
</Border>
</StackPanel>
</Page>

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using Windows.Storage.Pickers;
using Windows.UI.Xaml;
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
{
public NewVm Model => (NewVm)DataContext;
public NewDatabasePage()
{
InitializeComponent();
}
private async void ButtonBase_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;
Model.OpenFile(file);
}
}
}

View File

@@ -0,0 +1,38 @@
<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:local="using:ModernKeePass.Controls"
xmlns:converters="using:ModernKeePass.Converters"
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">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Page.Resources>
<Page.DataContext>
<viewModels:OpenVm/>
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<HyperlinkButton x:Uid="OpenBrowseButton" Click="ButtonBase_OnClick" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" x:Uid="OpenBrowseDesc" />
<HyperlinkButton x:Uid="OpenUrlButton" IsEnabled="False" />
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" x:Uid="OpenUrlDesc" />
<Border HorizontalAlignment="Left" BorderThickness="1" BorderBrush="AliceBlue" Width="550" Visibility="{Binding ShowPasswordBox, Converter={StaticResource BooleanToVisibilityConverter}}">
<StackPanel Margin="25,0,25,0">
<TextBlock Text="{Binding Name}" />
<userControls:CompositeKeyUserControl x:Uid="CompositeKeyOpenButton">
<interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="ValidationChecked">
<Core:NavigateToPageAction TargetPage="ModernKeePass.Views.GroupDetailPage" />
</Core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</userControls:CompositeKeyUserControl>
</StackPanel>
</Border>
</StackPanel>
</Page>

View File

@@ -0,0 +1,48 @@
using System;
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 OpenDatabasePage
{
private Frame _mainFrame;
public OpenVm Model => (OpenVm)DataContext;
public OpenDatabasePage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
_mainFrame = e.Parameter as Frame;
}
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;
Model.OpenFile(file);
}
}
}

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:viewModels="using:ModernKeePass.ViewModels"
xmlns:local="using:ModernKeePass.Controls"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:userControls="using:ModernKeePass.Views.UserControls"
x:Class="ModernKeePass.Views.RecentDatabasesPage"
mc:Ignorable="d">
<Page.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<CollectionViewSource x:Name="RecentItemsSource" Source="{Binding RecentItems}" />
</Page.Resources>
<Page.DataContext>
<viewModels:RecentVm/>
</Page.DataContext>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<HyperlinkButton Grid.Row="0" HorizontalAlignment="Right">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="Cancel" />
<TextBlock x:Uid="RecentClear" VerticalAlignment="Center" Margin="10,0,0,0" />
</StackPanel>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:CallMethodAction TargetObject="{Binding}" MethodName="ClearAll" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</HyperlinkButton>
<ListView Grid.Row="1"
ItemsSource="{Binding Source={StaticResource RecentItemsSource}}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="10,0,10,0">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Name}" Padding="5,0,0,0" />
<TextBlock Grid.Row="1" Text="{Binding Path}" Padding="5,0,0,0" FontSize="10" />
<userControls:CompositeKeyUserControl Grid.Row="2" x:Name="DatabaseUserControl" x:Uid="CompositeKeyOpenButton" HorizontalAlignment="Stretch" MinWidth="400" Margin="0,10,0,0" Visibility="{Binding IsSelected, Converter={StaticResource BooleanToVisibilityConverter}}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ValidationChecking">
<core:CallMethodAction TargetObject="{Binding}" MethodName="OpenDatabaseFile" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="ValidationChecked">
<core:CallMethodAction TargetObject="{Binding}" MethodName="UpdateAccessTime" />
<core:NavigateToPageAction TargetPage="ModernKeePass.Views.GroupDetailPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</userControls:CompositeKeyUserControl>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>

View File

@@ -0,0 +1,15 @@
// Pour en savoir plus sur le modèle d'élément Page vierge, consultez la page http://go.microsoft.com/fwlink/?LinkId=234238
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
{
public RecentDatabasesPage()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,19 @@
<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.SaveDatabasePage"
mc:Ignorable="d">
<Page.DataContext>
<viewModels:SaveVm/>
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<HyperlinkButton x:Uid="SaveButton" Click="SaveButton_OnClick" />
<TextBlock x:Uid="SaveDesc" Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" />
<HyperlinkButton x:Uid="SaveAsButton" Click="SaveAsButton_OnClick" />
<TextBlock x:Uid="SaveAsDesc" Style="{StaticResource BodyTextBlockStyle}" Margin="15,0,0,30" />
</StackPanel>
</Page>

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
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 SaveVm Model => (SaveVm)DataContext;
public SaveDatabasePage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
_mainFrame = e.Parameter as Frame;
}
private void SaveButton_OnClick(object sender, RoutedEventArgs e)
{
Model.Save();
_mainFrame.Navigate(typeof(Views.MainPage));
}
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;
Model.Save(file);
_mainFrame.Navigate(typeof(Views.MainPage));
}
}
}

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,2,0,10">
<SymbolIcon Symbol="Back" Margin="0,7,40,0" />
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}" x:Uid="WelcomeOpen" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,6,0,10">
<SymbolIcon Symbol="Back" Margin="0,7,40,0" />
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}" 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,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,30 @@
using Windows.UI.Xaml.Controls;
using ModernKeePass.ViewModels;
// 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 SettingsVm Model => (SettingsVm)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 ListMenuItemVm;
MenuFrame?.Navigate(selectedItem == null ? typeof(SettingsWelcomePage) : selectedItem.PageType);
}
}
}

View File

@@ -0,0 +1,44 @@
<Page
x:Class="ModernKeePass.Views.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"
xmlns:viewModels="using:ModernKeePass.ViewModels"
xmlns:templateSelectors="using:ModernKeePass.TemplateSelectors"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource x:Name="RecycleBinGroups" Source="{Binding Groups}" />
<CollectionViewSource x:Name="Ciphers" Source="{Binding Ciphers}" />
<CollectionViewSource x:Name="Compressions" Source="{Binding Compressions}" />
<CollectionViewSource x:Name="KeyDerivations" Source="{Binding KeyDerivations}" />
</Page.Resources>
<Page.DataContext>
<viewModels:SettingsDatabaseVm />
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleSwitch x:Uid="SettingsDatabaseRecycleBin" IsOn="{Binding HasRecycleBin, Mode=TwoWay}" />
<ComboBox ItemsSource="{Binding Source={StaticResource RecycleBinGroups}}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" IsEnabled="{Binding HasRecycleBin}">
<ComboBox.Resources>
<DataTemplate x:Name="GroupFirstItem">
<TextBlock x:Uid="GroupNewItemTextBox" />
</DataTemplate>
<DataTemplate x:Name="GroupOtherItem">
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.Resources>
<ComboBox.ItemTemplateSelector>
<templateSelectors:FirstItemDataTemplateSelector
FirstItem="{StaticResource GroupFirstItem}"
OtherItem="{StaticResource GroupOtherItem}" />
</ComboBox.ItemTemplateSelector>
</ComboBox>
<TextBlock x:Uid="SettingsDatabaseEncryption" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />
<ComboBox ItemsSource="{Binding Source={StaticResource Ciphers}}" SelectedIndex="{Binding CipherIndex, Mode=TwoWay}" />
<TextBlock x:Uid="SettingsDatabaseCompression" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />
<ComboBox ItemsSource="{Binding Source={StaticResource Compressions}}" SelectedItem="{Binding CompressionName, Mode=TwoWay}" />
<TextBlock x:Uid="SettingsDatabaseKdf" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,20,0,10" />
<ComboBox ItemsSource="{Binding Source={StaticResource KeyDerivations}}" SelectedItem="{Binding KeyDerivationName, Mode=TwoWay}" />
</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 SettingsDatabasePage
{
public SettingsDatabasePage()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,40 @@
<Page
x:Class="ModernKeePass.Views.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"
xmlns:viewModels="using:ModernKeePass.ViewModels"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource x:Name="KeyDerivations" Source="{Binding FileFormats}" />
</Page.Resources>
<Page.DataContext>
<viewModels:SettingsNewVm />
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Uid="SettingsNewDatabaseDesc" Style="{StaticResource TextBlockSettingsHeaderStyle}" Margin="5,0,0,10"/>
<ToggleSwitch x:Uid="SettingsNewDatabaseSample" IsOn="{Binding IsCreateSample, Mode=TwoWay}" />
<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="{Binding Source={StaticResource KeyDerivations}}" SelectedItem="{Binding FileFormatVersion, Mode=TwoWay}" />
<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,15 @@
// Pour en savoir plus sur le modèle d'élément Page vierge, consultez la page http://go.microsoft.com/fwlink/?LinkId=234238
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 SettingsNewDatabasePage
{
public SettingsNewDatabasePage()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,20 @@
<Page
x:Class="ModernKeePass.Views.SettingsSecurityPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ModernKeePass.Controls"
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}">
<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:CompositeKeyUserControl Margin="0,20,0,0" UpdateKey="True" x:Uid="SettingsSecurityUpdateButton" ValidationChecked="CompositeKeyUserControl_OnValidationChecked" />
</StackPanel>
</Page>

View File

@@ -0,0 +1,23 @@
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
using ModernKeePass.Common;
using ModernKeePass.Events;
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 SettingsSecurityPage
{
public SettingsSecurityPage()
{
InitializeComponent();
}
private void CompositeKeyUserControl_OnValidationChecked(object sender, PasswordEventArgs e)
{
ToastNotificationHelper.ShowGenericToast("Composite key", "Database successfully updated.");
}
}
}

View File

@@ -0,0 +1,14 @@
<Page
x:Class="ModernKeePass.Views.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
{
/// <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,61 @@
<UserControl x:Name="UserControl"
x:Class="ModernKeePass.Views.UserControls.CompositeKeyUserControl"
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:actions="using:ModernKeePass.Actions"
xmlns:converters="using:ModernKeePass.Converters"
xmlns:viewModels="using:ModernKeePass.ViewModels"
mc:Ignorable="d" >
<UserControl.Resources>
<SolidColorBrush x:Key="ErrorColorBrush" Color="Red"/>
<converters:ProgressBarLegalValuesConverter x:Key="ProgressBarLegalValuesConverter"/>
<converters:DoubleToSolidColorBrushConverter x:Key="DoubleToForegroungBrushConverter"/>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<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:CompositeKeyVm />
</Grid.DataContext>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="45" />
<RowDefinition Height="40" />
<RowDefinition Height="40" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" IsChecked="{Binding HasPassword, Mode=TwoWay}" />
<PasswordBox Grid.Row="0" Grid.Column="1" x:Name="PasswordBox" x:Uid="CompositeKeyPassword" Password="{Binding Password, Mode=TwoWay}" Height="30" IsEnabled="{Binding HasPassword}" IsPasswordRevealButtonEnabled="True" KeyDown="PasswordBox_KeyDown" BorderBrush="{Binding StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" >
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding HasPassword}" Value="True">
<actions:SetupFocusAction TargetObject="{Binding ElementName=PasswordBox}" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</PasswordBox>
<ProgressBar Grid.Row="0" Grid.Column="1"
Maximum="128" VerticalAlignment="Bottom"
Value="{Binding PasswordComplexityIndicator, ConverterParameter=0\,128, Converter={StaticResource ProgressBarLegalValuesConverter}}"
Foreground="{Binding PasswordComplexityIndicator, ConverterParameter=128, Converter={StaticResource DoubleToForegroungBrushConverter}}"
Visibility="{Binding ShowComplexityIndicator, ElementName=UserControl, Converter={StaticResource BooleanToVisibilityConverter}}" />
<CheckBox Grid.Row="1" Grid.Column="0" IsChecked="{Binding HasKeyFile, Mode=TwoWay}" />
<HyperlinkButton Grid.Row="1" Grid.Column="1" Margin="-15,0,0,0" Content="{Binding KeyFileText}" IsEnabled="{Binding HasKeyFile}" Click="KeyFileButton_Click" />
<HyperlinkButton Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Visibility="{Binding ShowComplexityIndicator, ElementName=UserControl, Converter={StaticResource BooleanToVisibilityConverter}}" IsEnabled="{Binding HasKeyFile}" Click="CreateKeyFileButton_Click">
<SymbolIcon Symbol="Add">
<ToolTipService.ToolTip>
<ToolTip x:Uid="CompositeKeyNewKeyFileTooltip" />
</ToolTipService.ToolTip>
</SymbolIcon>
</HyperlinkButton>
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="3" Height="28" FontSize="14" FontWeight="Light" Text="{Binding Status}" Foreground="{Binding StatusType, Converter={StaticResource DiscreteIntToSolidColorBrushConverter}}" Visibility="{Binding Status, Converter={StaticResource EmptyStringToVisibilityConverter}}" />
<Button Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" Content="{Binding ButtonLabel, ElementName=UserControl}" Click="OpenButton_OnClick" Background="{ThemeResource ListViewItemSelectedPointerOverBorderThemeBrush}" Foreground="{ThemeResource TextBoxBackgroundThemeBrush}" IsEnabled="{Binding IsValid}" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using Windows.Storage.Pickers;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
using ModernKeePass.Events;
using ModernKeePass.Extensions;
using ModernKeePass.Services;
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 CompositeKeyUserControl
{
public CompositeKeyVm Model => Grid.DataContext as CompositeKeyVm;
public bool CreateNew
{
get { return (bool)GetValue(CreateNewProperty); }
set { SetValue(CreateNewProperty, value); }
}
public static readonly DependencyProperty CreateNewProperty =
DependencyProperty.Register(
"CreateNew",
typeof(bool),
typeof(CompositeKeyUserControl),
new PropertyMetadata(false, (o, args) => { }));
public bool UpdateKey
{
get { return (bool)GetValue(UpdateKeyProperty); }
set { SetValue(UpdateKeyProperty, value); }
}
public static readonly DependencyProperty UpdateKeyProperty =
DependencyProperty.Register(
"UpdateKey",
typeof(bool),
typeof(CompositeKeyUserControl),
new PropertyMetadata(false, (o, args) => { }));
public string ButtonLabel
{
get { return (string)GetValue(ButtonLabelProperty); }
set { SetValue(ButtonLabelProperty, value); }
}
public static readonly DependencyProperty ButtonLabelProperty =
DependencyProperty.Register(
"ButtonLabel",
typeof(string),
typeof(CompositeKeyUserControl),
new PropertyMetadata("OK", (o, args) => { }));
public bool ShowComplexityIndicator => CreateNew || UpdateKey;
public CompositeKeyUserControl()
{
InitializeComponent();
}
public event PasswordCheckingEventHandler ValidationChecking;
public delegate void PasswordCheckingEventHandler(object sender, EventArgs e);
public event PasswordCheckedEventHandler ValidationChecked;
public delegate void PasswordCheckedEventHandler(object sender, PasswordEventArgs e);
private async void OpenButton_OnClick(object sender, RoutedEventArgs e)
{
ValidationChecking?.Invoke(this, new EventArgs());
if (UpdateKey)
{
Model.UpdateKey();
ValidationChecked?.Invoke(this, new PasswordEventArgs(Model.RootGroup));
}
else
{
var resource = new ResourcesService();
var oldLabel = ButtonLabel;
ButtonLabel = resource.GetResourceValue("CompositeKeyOpening");
if (await Dispatcher.RunTaskAsync(async () => await Model.OpenDatabase(CreateNew)))
{
ValidationChecked?.Invoke(this, new PasswordEventArgs(Model.RootGroup));
}
ButtonLabel = oldLabel;
}
}
private void PasswordBox_KeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Enter && Model.IsValid) OpenButton_OnClick(null, null);
}
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;
Model.KeyFile = file;
}
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;
Model.CreateKeyFile(file);
}
}
}