New Database settings page

Implements Recycle Bin (new group creation still needs to be implemented)
Code refactoring
This commit is contained in:
2017-10-30 18:34:38 +01:00
committed by BONNEVILLE Geoffroy
parent fa3d38db18
commit 699452667c
22 changed files with 395 additions and 459 deletions

View File

@@ -16,13 +16,31 @@ namespace ModernKeePass.Common
Opening = 1,
Opened = 2
}
private PwDatabase _pwDatabase = new PwDatabase();
private readonly PwDatabase _pwDatabase = new PwDatabase();
private StorageFile _databaseFile;
private GroupVm _recycleBin;
public GroupVm RootGroup { get; set; }
public GroupVm RecycleBin
{
get { return _recycleBin; }
set
{
_recycleBin = value;
_pwDatabase.RecycleBinUuid = _recycleBin.IdUuid;
}
}
public DatabaseStatus Status { get; private set; } = DatabaseStatus.Closed;
public string Name => DatabaseFile?.Name;
public bool RecycleBinEnabled
{
get { return _pwDatabase.RecycleBinEnabled; }
set { _pwDatabase.RecycleBinEnabled = value; }
}
public StorageFile DatabaseFile
{
get { return _databaseFile; }
@@ -52,7 +70,7 @@ namespace ModernKeePass.Common
if (_pwDatabase.IsOpen)
{
Status = DatabaseStatus.Opened;
RootGroup = new GroupVm(_pwDatabase.RootGroup, null);
RootGroup = new GroupVm(_pwDatabase.RootGroup, null, RecycleBinEnabled ? _pwDatabase.RecycleBinUuid : null);
}
}
catch (ArgumentNullException)

View File

@@ -1,19 +1,15 @@
using System;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.UI.Text;
using Windows.UI.Xaml.Data;
namespace ModernKeePass.Converters
{
public class TextToFontStyleConverter : IValueConverter
public class BooleanToFontStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var compareValue = parameter as string;
var text = value as string;
return string.Compare(text, compareValue, StringComparison.OrdinalIgnoreCase) == 0
? FontStyle.Italic
: FontStyle.Normal;
var boolean = value is bool ? (bool)value : false;
return boolean ? FontStyle.Italic : FontStyle.Normal;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)

View File

@@ -11,9 +11,21 @@ namespace ModernKeePass.Interfaces
string Name { get; set; }
bool IsEditMode { get; }
/// <summary>
/// Delete from Model
/// </summary>
void CommitDelete();
/// <summary>
/// Restore ViewModel
/// </summary>
void UndoDelete();
/// <summary>
/// Save changes to Model
/// </summary>
void Save();
/// <summary>
/// Delete from ViewModel
/// </summary>
void MarkForDelete();
}
}

View File

@@ -123,6 +123,9 @@
<Compile Include="Interfaces\IHasSelectableObject.cs" />
<Compile Include="Interfaces\ISelectableModel.cs" />
<Compile Include="Pages\BasePages\LayoutAwarePageBase.cs" />
<Compile Include="Pages\SettingsPageFrames\SettingsDatabasePage.xaml.cs">
<DependentUpon>SettingsDatabasePage.xaml</DependentUpon>
</Compile>
<Compile Include="TemplateSelectors\FirstItemDataTemplateSelector.cs" />
<Compile Include="Controls\ListViewWithDisable.cs" />
<Compile Include="Controls\OpenDatabaseUserControl.xaml.cs">
@@ -133,7 +136,7 @@
<Compile Include="Converters\ColorToBrushConverter.cs" />
<Compile Include="Converters\DoubleToForegroungBrushComplexityConverter.cs" />
<Compile Include="Converters\InverseBooleanToVisibilityConverter.cs" />
<Compile Include="Converters\TextToFontStyleConverter.cs" />
<Compile Include="Converters\BooleanToFontStyleConverter.cs" />
<Compile Include="Converters\PluralizationConverter.cs" />
<Compile Include="Converters\ProgressBarLegalValuesConverter.cs" />
<Compile Include="Converters\TextToWidthConverter.cs" />
@@ -157,6 +160,7 @@
<DependentUpon>WelcomePage.xaml</DependentUpon>
</Compile>
<Compile Include="ViewModels\AboutVm.cs" />
<Compile Include="ViewModels\Items\ListMenuItemVm.cs" />
<Compile Include="ViewModels\Items\MainMenuItemVm.cs" />
<Compile Include="ViewModels\Items\RecentItemVm.cs" />
<Compile Include="Pages\EntryDetailPage.xaml.cs">
@@ -177,11 +181,13 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ViewModels\EntryVm.cs" />
<Compile Include="ViewModels\GroupVm.cs" />
<Compile Include="ViewModels\Items\SettingsVM.cs" />
<Compile Include="ViewModels\MainVm.cs" />
<Compile Include="ViewModels\NewVm.cs" />
<Compile Include="ViewModels\OpenVm.cs" />
<Compile Include="ViewModels\RecentVm.cs" />
<Compile Include="ViewModels\SaveVm.cs" />
<Compile Include="ViewModels\SettingsDatabaseVm.cs" />
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
@@ -245,6 +251,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Pages\SettingsPageFrames\SettingsDatabasePage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Styles\HamburgerButtonStyle.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -341,9 +351,7 @@
<Name>ModernKeePassLib</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Pages\SettingsPageFrames\" />
</ItemGroup>
<ItemGroup />
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '12.0' ">
<VisualStudioVersion>12.0</VisualStudioVersion>
</PropertyGroup>

View File

@@ -5,7 +5,6 @@ using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Navigation;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.ViewModels;
namespace ModernKeePass.Pages.BasePages
{

View File

@@ -379,13 +379,13 @@
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<!--<AppBarButton Icon="Setting" Label="Settings">
<AppBarButton Icon="Setting" Label="Settings">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Pages.SettingsPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>-->
</AppBarButton>
</CommandBar.SecondaryCommands>
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>

View File

@@ -75,7 +75,11 @@ namespace ModernKeePass.Pages
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
MessageDialogHelper.ShowDeleteConfirmationDialog("Are you sure you want to delete this entry?", Model, Frame);
var app = (App)Application.Current;
var message = app.Database.RecycleBinEnabled
? "Are you sure you want to send this entry to the recycle bin?"
: "Are you sure you want to delete this entry?";
MessageDialogHelper.ShowDeleteConfirmationDialog(message, Model, Frame);
}
private async void UrlButton_Click(object sender, RoutedEventArgs e)

View File

@@ -16,7 +16,7 @@
<SolidColorBrush x:Key="Transparent" Color="Transparent"/>
<SolidColorBrush x:Key="SystemColor" Color="{StaticResource SystemColorButtonFaceColor}" />
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter"/>
<converters:TextToFontStyleConverter x:Key="TextToFontStyleConverter"/>
<converters:BooleanToFontStyleConverter x:Key="BooleanToFontStyleConverter"/>
</Page.Resources>
<Page.DataContext>
<viewModels:GroupVm />
@@ -32,13 +32,13 @@
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>
<!--<AppBarButton Icon="Setting" Label="Settings">
<AppBarButton Icon="Setting" Label="Settings">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Click">
<core:NavigateToPageAction TargetPage="ModernKeePass.Pages.SettingsPage" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AppBarButton>-->
</AppBarButton>
</CommandBar.SecondaryCommands>
<AppBarToggleButton Icon="Edit" Label="Edit" IsChecked="{Binding IsEditMode, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
@@ -101,8 +101,7 @@
<DataTemplate x:Name="Expanded">
<StackPanel Orientation="Horizontal">
<SymbolIcon Symbol="{Binding IconSymbol}" Margin="8,0,0,0" />
<TextBlock Text="{Binding Name}" FontWeight="{Binding FontWeight}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" FontStyle="{Binding Name, ConverterParameter=Recycle Bin, Converter={StaticResource TextToFontStyleConverter}}" />
<!--<TextBlock Text="{Binding EntryCount}" HorizontalAlignment="Right" VerticalAlignment="Center" />-->
<TextBlock Text="{Binding Name}" FontWeight="{Binding FontWeight}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="30,0,20,0" FontStyle="{Binding IsSelected, Converter={StaticResource BooleanToFontStyleConverter}}" />
</StackPanel>
</DataTemplate>
</ListView.Resources>
@@ -161,12 +160,12 @@
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<SymbolIcon Grid.Column="0" Symbol="{Binding IconSymbol}" Width="100" Height="100" RenderTransformOrigin="0.5,0.5" Foreground="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}" >
<SymbolIcon Grid.Column="0" Symbol="{Binding IconSymbol}" Width="100" Height="100" RenderTransformOrigin="0.5,0.5" Foreground="{ThemeResource TextBoxBackgroundThemeBrush}" >
<SymbolIcon.RenderTransform>
<CompositeTransform ScaleX="2" TranslateX="0" TranslateY="0" ScaleY="2"/>
</SymbolIcon.RenderTransform>
</SymbolIcon>
<TextBlock Grid.Column="1" Text="{Binding Name}" FontWeight="Bold" Style="{ThemeResource TitleTextBlockStyle}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="13,0,0,5"/>
<TextBlock Grid.Column="1" Text="{Binding Name}" FontWeight="Bold" Style="{ThemeResource TitleTextBlockStyle}" Foreground="{ThemeResource TextBoxBackgroundThemeBrush}" TextWrapping="NoWrap" VerticalAlignment="Center" Margin="13,0,0,5"/>
</Grid>
</Border>
</DataTemplate>
@@ -189,10 +188,7 @@
</Border>
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,10,0,0" >
<TextBlock x:Name="NameTextBlock" Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" Foreground="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}"/>
<!--<Line Visibility="{Binding HasExpired, Converter={StaticResource BooleanToVisibilityConverter}}" Margin="0,-10,0,0" Stretch="Fill" Stroke="{Binding ForegroundColor, ConverterParameter={StaticResource TextBoxForegroundThemeBrush}, Converter={StaticResource ColorToBrushConverter}}" StrokeThickness="1" X1="1" Width="{Binding Name, Converter={StaticResource TextToWidthConverter}, ConverterParameter=7}" HorizontalAlignment="Left" VerticalAlignment="Center" />-->
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
<!--<TextBlock Text="{Binding EntryCount, ConverterParameter=entry\,entries, Converter={StaticResource PluralizationConverter}}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
<TextBlock Text="{Binding GroupCount, ConverterParameter=group\,groups, Converter={StaticResource PluralizationConverter}}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />-->
<TextBlock Text="{Binding UserName}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
<TextBlock Text="{Binding Url}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60" />
</StackPanel>

View File

@@ -1,9 +1,6 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
@@ -115,7 +112,11 @@ namespace ModernKeePass.Pages
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
MessageDialogHelper.ShowDeleteConfirmationDialog("Are you sure you want to delete the whole group and all its entries?", Model, Frame);
var app = (App) Application.Current;
var message = app.Database.RecycleBinEnabled
? "Are you sure you want to send the whole group and all its entries to the recycle bin?"
: "Are you sure you want to delete the whole group and all its entries?";
MessageDialogHelper.ShowDeleteConfirmationDialog(message, Model, Frame);
}
private void SemanticZoom_ViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)

View File

@@ -130,12 +130,6 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuListView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuFrame" Storyboard.TargetProperty="(Grid.Row)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuFrame" Storyboard.TargetProperty="(Grid.RowSpan)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>-->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitlePanel" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
@@ -148,9 +142,6 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PageTitleTextBlock" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="60,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuFrame" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="10,0,0,0"/>
</ObjectAnimationUsingKeyFrames>-->
</Storyboard>
</VisualState>
</VisualStateGroup>

View File

@@ -1,144 +1,111 @@
<Page
x:Name="pageRoot"
x:Class="ModernKeePass.Pages.SettingsPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
<basePages:LayoutAwarePageBase
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ModernKeePass.Pages"
xmlns:common="using:ModernKeePass.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:basePages="using:ModernKeePass.Pages.BasePages"
xmlns:controls="using:ModernKeePass.Controls"
xmlns:viewModels="using:ModernKeePass.ViewModels"
x:Name="PageRoot"
x:Class="ModernKeePass.Pages.SettingsPage"
mc:Ignorable="d">
<Page.Resources>
<!-- Collection of items displayed by this page -->
<CollectionViewSource
x:Name="itemsViewSource"
Source="{Binding Items}"/>
<CollectionViewSource x:Name="MenuItemsSource" Source="{Binding MenuItems}" >
</CollectionViewSource>
</Page.Resources>
<Page.DataContext>
<viewModels:SettingsVM />
</Page.DataContext>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Background>
<StaticResource ResourceKey="ApplicationPageBackgroundThemeBrush"/>
</Page.Background>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="primaryColumn" Width="420"/>
<ColumnDefinition x:Name="secondaryColumn" Width="*"/>
<ColumnDefinition x:Name="PrimaryColumn" Width="250" />
<ColumnDefinition x:Name="SecondaryColumn" Width="*" />
</Grid.ColumnDefinitions>
<!-- Back button and page title -->
<Grid x:Name="titlePanel">
<Grid x:Name="TitlePanel" Background="{ThemeResource AppBarBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
<Button x:Name="BackButton"
Command="{Binding NavigationHelper.GoBackCommand, ElementName=PageRoot}"
Height="40"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"/>
<TextBlock x:Name="pageTitle" Text="{Binding Title}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,0,40"/>
AutomationProperties.ItemType="Navigation Button"
Style="{StaticResource NoBorderButtonStyle}">
<SymbolIcon Symbol="Back" />
</Button>
<TextBlock x:Name="TitleTextBox" Text="Settings" Grid.Column="1" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<!-- Vertical scrolling item list -->
<ListView
x:Name="itemListView"
AutomationProperties.AutomationId="ItemsListView"
AutomationProperties.Name="Items"
TabIndex="1"
<controls:ListViewWithDisable
Grid.Column="0"
Grid.Row="1"
Margin="-10,-10,0,0"
Padding="120,0,0,60"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
IsSwipeEnabled="False"
SelectionChanged="ItemListView_SelectionChanged">
<ListView.ItemTemplate>
x:Name="MenuListView"
SelectionChanged="MenuListView_SelectionChanged"
Background="{ThemeResource AppBarBackgroundThemeBrush}"
ItemsSource="{Binding Source={StaticResource MenuItemsSource}}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="False"
ItemContainerStyle="{StaticResource ListViewLeftIndicatorItemExpanded}">
<controls:ListViewWithDisable.ItemTemplate>
<DataTemplate>
<Grid Margin="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="60" Height="60">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel Grid.Column="1" Margin="10,0,0,0">
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap" MaxHeight="40"/>
<TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
<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>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="FrameworkElement">
<Setter Property="Margin" Value="0,0,0,10"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
<!-- Details for selected item -->
<ScrollViewer
x:Name="itemDetail"
AutomationProperties.AutomationId="ItemDetailScrollViewer"
Grid.Column="1"
Grid.RowSpan="2"
Padding="60,0,66,0"
DataContext="{Binding SelectedItem, ElementName=itemListView}"
HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollMode="Disabled" ScrollViewer.VerticalScrollMode="Enabled"
ScrollViewer.ZoomMode="Disabled">
<Grid x:Name="itemDetailGrid" Margin="0,60,0,50">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Row="1" Margin="0,0,20,0" Width="180" Height="180" Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
<StackPanel x:Name="itemDetailTitlePanel" Grid.Row="1" Grid.Column="1">
<TextBlock x:Name="itemTitle" Margin="0,-10,0,0" Text="{Binding Title}" Style="{StaticResource SubheaderTextBlockStyle}"/>
<TextBlock x:Name="itemSubtitle" Margin="0,0,0,20" Text="{Binding Subtitle}" Style="{StaticResource SubtitleTextBlockStyle}"/>
</StackPanel>
<TextBlock Grid.Row="2" Grid.ColumnSpan="2" Margin="0,20,0,0" Text="{Binding Content}" Style="{StaticResource BodyTextBlockStyle}"/>
</Grid>
</ScrollViewer>
</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">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PrimaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="*"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="secondaryColumn" Storyboard.TargetProperty="Width">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SecondaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Visibility">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuFrame" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Padding">
<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>
@@ -152,30 +119,27 @@
-->
<VisualState x:Name="SinglePane_Detail">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="primaryColumn" Storyboard.TargetProperty="Width">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PrimaryColumn" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Visibility">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="MenuListView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.Row)">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitlePanel" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="(Grid.RowSpan)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="titlePanel" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleTextBox" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetailGrid" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,0,0,60"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemDetail" Storyboard.TargetProperty="Padding">
<DiscreteObjectKeyFrame KeyTime="0" Value="120,0,90,0"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PageTitleTextBlock" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="60,0,0,0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</Page>
</basePages:LayoutAwarePageBase>

View File

@@ -1,20 +1,5 @@
using ModernKeePass.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Windows.Input;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Controls;
using ModernKeePass.ViewModels;
// The Split Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234234
@@ -24,232 +9,23 @@ namespace ModernKeePass.Pages
/// 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 : Page
public sealed partial class SettingsPage
{
private NavigationHelper navigationHelper;
private ObservableDictionary defaultViewModel = new ObservableDictionary();
/// <summary>
/// This can be changed to a strongly typed view model.
/// </summary>
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
/// <summary>
/// NavigationHelper is used on each page to aid in navigation and
/// process lifetime management
/// </summary>
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
public new SettingsVM Model => (SettingsVM)DataContext;
public SettingsPage()
{
this.InitializeComponent();
// Setup the navigation helper
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
this.navigationHelper.SaveState += navigationHelper_SaveState;
// Setup the logical page navigation components that allow
// the page to only show one pane at a time.
this.navigationHelper.GoBackCommand = new ModernKeePass.Common.RelayCommand(() => this.GoBack(), () => this.CanGoBack());
this.itemListView.SelectionChanged += itemListView_SelectionChanged;
// Start listening for Window size changes
// to change from showing two panes to showing a single pane
Window.Current.SizeChanged += Window_SizeChanged;
this.InvalidateVisualState();
InitializeComponent();
ListView = MenuListView;
ListViewSource = MenuItemsSource;
}
void itemListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
private void MenuListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this.UsingLogicalPageNavigation())
{
this.navigationHelper.GoBackCommand.RaiseCanExecuteChanged();
}
}
/// <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)
{
// 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 (!this.UsingLogicalPageNavigation() && this.itemsViewSource.View != null)
{
this.itemsViewSource.View.MoveCurrentToFirst();
}
}
else
{
// Restore the previously saved state associated with this page
if (e.PageState.ContainsKey("SelectedItem") && this.itemsViewSource.View != null)
{
// TODO: Invoke Me.itemsViewSource.View.MoveCurrentTo() with the selected
// item as specified by the value of pageState("SelectedItem")
ListView_SelectionChanged(sender, e);
var selectedItem = Model.SelectedItem as ListMenuItemVm;
if (selectedItem == null) return;
MenuFrame?.Navigate(selectedItem.PageType);
}
}
}
/// <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>
private void navigationHelper_SaveState(object sender, SaveStateEventArgs e)
{
if (this.itemsViewSource.View != null)
{
// TODO: Derive a serializable navigation parameter and assign it to
// pageState("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.
private 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>
private 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>
private void Window_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
this.InvalidateVisualState();
}
/// <summary>
/// Invoked when an item within the list is selected.
/// </summary>
/// <param name="sender">The GridView displaying the selected item.</param>
/// <param name="e">Event data that describes how the selection was changed.</param>
private void ItemListView_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 (this.UsingLogicalPageNavigation()) this.InvalidateVisualState();
}
private bool CanGoBack()
{
if (this.UsingLogicalPageNavigation() && this.itemListView.SelectedItem != null)
{
return true;
}
else
{
return this.navigationHelper.CanGoBack();
}
}
private void GoBack()
{
if (this.UsingLogicalPageNavigation() && this.itemListView.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.
this.itemListView.SelectedItem = null;
}
else
{
this.navigationHelper.GoBack();
}
}
private void InvalidateVisualState()
{
var visualState = DetermineVisualState();
VisualStateManager.GoToState(this, visualState, false);
this.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>
private string DetermineVisualState()
{
if (!UsingLogicalPageNavigation())
return "PrimaryView";
// Update the back button's enabled state when the view state changes
var logicalPageBack = this.UsingLogicalPageNavigation() && this.itemListView.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,22 @@
<Page
x:Class="ModernKeePass.Pages.SettingsDatabasePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ModernKeePass.Pages"
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="RecycleBinGroups" Source="{Binding Groups}" />
</Page.Resources>
<Page.DataContext>
<viewModels:SettingsDatabaseVm />
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleSwitch Header="Recycle bin" OffContent="Disabled" OnContent="Enabled" IsOn="{Binding HasRecycleBin, Mode=TwoWay}" />
<ComboBox ItemsSource="{Binding Source={StaticResource RecycleBinGroups}}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" IsEnabled="{Binding HasRecycleBin}" />
</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.Pages
{
/// <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

@@ -14,13 +14,13 @@ namespace ModernKeePass.ViewModels
public class EntryVm : INotifyPropertyChanged, IPwEntity
{
public GroupVm ParentGroup { get; }
public PwEntry Entry { get; }
public System.Drawing.Color? BackgroundColor => Entry?.BackgroundColor;
public System.Drawing.Color? ForegroundColor => Entry?.ForegroundColor;
public System.Drawing.Color? BackgroundColor => _pwEntry?.BackgroundColor;
public System.Drawing.Color? ForegroundColor => _pwEntry?.ForegroundColor;
public bool IsRevealPasswordEnabled => !string.IsNullOrEmpty(Password);
public bool HasExpired => HasExpirationDate && Entry.ExpiryTime < DateTime.Now;
public bool HasExpired => HasExpirationDate && _pwEntry.ExpiryTime < DateTime.Now;
public double PasswordComplexityIndicator => QualityEstimation.EstimatePasswordBits(Password.ToCharArray());
public bool IsFirstItem => _pwEntry == null;
public double PasswordLength { get; set; } = 25;
public bool UpperCasePatternSelected { get; set; } = true;
@@ -38,12 +38,12 @@ namespace ModernKeePass.ViewModels
get
{
var title = GetEntryValue(PwDefs.TitleField);
return title == null ? "New entry" : title;
return title == null ? "< New entry >" : title;
}
set { SetEntryValue(PwDefs.TitleField, value); }
}
public string Id => Entry.Uuid.ToHexString();
public string Id => _pwEntry?.Uuid.ToHexString();
public string UserName
{
@@ -75,22 +75,22 @@ namespace ModernKeePass.ViewModels
{
get
{
if (Entry == null) return Symbol.Add;
if (_pwEntry == null) return Symbol.Add;
if (HasExpired) return Symbol.Priority;
var result = PwIconToSegoeMapping.GetSymbolFromIcon(Entry.IconId);
var result = PwIconToSegoeMapping.GetSymbolFromIcon(_pwEntry.IconId);
return result == Symbol.More ? Symbol.Permissions : result;
}
}
public DateTimeOffset ExpiryDate
{
get { return new DateTimeOffset(Entry.ExpiryTime.Date); }
set { if (HasExpirationDate) Entry.ExpiryTime = value.DateTime; }
get { return new DateTimeOffset(_pwEntry.ExpiryTime.Date); }
set { if (HasExpirationDate) _pwEntry.ExpiryTime = value.DateTime; }
}
public TimeSpan ExpiryTime
{
get { return Entry.ExpiryTime.TimeOfDay; }
set { if (HasExpirationDate) Entry.ExpiryTime = Entry.ExpiryTime.Date.Add(value); }
get { return _pwEntry.ExpiryTime.TimeOfDay; }
set { if (HasExpirationDate) _pwEntry.ExpiryTime = _pwEntry.ExpiryTime.Date.Add(value); }
}
public bool IsEditMode
@@ -114,16 +114,17 @@ namespace ModernKeePass.ViewModels
}
public bool HasExpirationDate
{
get { return Entry.Expires; }
get { return _pwEntry.Expires; }
set
{
Entry.Expires = value;
_pwEntry.Expires = value;
NotifyPropertyChanged("HasExpirationDate");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private readonly PwEntry _pwEntry;
private bool _isEditMode;
private bool _isRevealPassword;
@@ -135,7 +136,7 @@ namespace ModernKeePass.ViewModels
public EntryVm() { }
public EntryVm(PwEntry entry, GroupVm parent)
{
Entry = entry;
_pwEntry = entry;
ParentGroup = parent;
}
@@ -163,7 +164,7 @@ namespace ModernKeePass.ViewModels
ProtectedString password;
PwGenerator.Generate(out password, pwProfile, null, new CustomPwGeneratorPool());
Entry?.Strings.Set(PwDefs.PasswordField, password);
_pwEntry?.Strings.Set(PwDefs.PasswordField, password);
NotifyPropertyChanged("Password");
NotifyPropertyChanged("IsRevealPasswordEnabled");
NotifyPropertyChanged("PasswordComplexityIndicator");
@@ -171,12 +172,12 @@ namespace ModernKeePass.ViewModels
private string GetEntryValue(string key)
{
return Entry?.Strings.GetSafe(key).ReadString();
return _pwEntry?.Strings.GetSafe(key).ReadString();
}
private void SetEntryValue(string key, string newValue)
{
Entry?.Strings.Set(key, new ProtectedString(true, newValue));
_pwEntry?.Strings.Set(key, new ProtectedString(true, newValue));
}
public void MarkForDelete()
@@ -187,7 +188,7 @@ namespace ModernKeePass.ViewModels
}
public void CommitDelete()
{
Entry.ParentGroup.Entries.Remove(Entry);
_pwEntry.ParentGroup.Entries.Remove(_pwEntry);
}
public void UndoDelete()

View File

@@ -7,11 +7,10 @@ using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.Mappings;
using ModernKeePassLib;
using System;
namespace ModernKeePass.ViewModels
{
public class GroupVm : NotifyPropertyChangedBase, IPwEntity
public class GroupVm : NotifyPropertyChangedBase, IPwEntity, ISelectableModel
{
public GroupVm ParentGroup { get; }
public ObservableCollection<EntryVm> Entries { get; set; } = new ObservableCollection<EntryVm>();
@@ -19,26 +18,33 @@ namespace ModernKeePass.ViewModels
public ObservableCollection<GroupVm> Groups { get; set; } = new ObservableCollection<GroupVm>();
public int EntryCount => Entries.Count() - 1;
public int GroupCount => Groups.Count - 1;
public bool IsNotRoot => ParentGroup != null;
public FontWeight FontWeight => _pwGroup == null ? FontWeights.Bold : FontWeights.Normal;
public string Id => _pwGroup.Uuid.ToHexString();
public int GroupCount => Groups.Count - 1;
public PwUuid IdUuid => _pwGroup?.Uuid;
public string Id => IdUuid?.ToHexString();
public bool IsNotRoot => ParentGroup != null;
/// <summary>
/// Is the Group the database Recycle Bin?
/// </summary>
public bool IsSelected
{
get { return _app.Database.RecycleBinEnabled && _app.Database.RecycleBin.Id == Id; }
set
{
// TODO: if _pwGroup is null, create a new group
if (value && _pwGroup != null) _app.Database.RecycleBin = this;
}
}
public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut
{
get
{
return from e in Entries
where e.Entry != null
public IOrderedEnumerable<IGrouping<char, EntryVm>> EntriesZoomedOut => from e in Entries
where !e.IsFirstItem
group e by e.Name.FirstOrDefault() into grp
orderby grp.Key
select grp;
}
}
public string Name
{
get { return _pwGroup == null ? "New group" : _pwGroup.Name; }
get { return _pwGroup == null ? "< New group >" : _pwGroup.Name; }
set { _pwGroup.Name = value; }
}
@@ -59,18 +65,20 @@ namespace ModernKeePass.ViewModels
}
private readonly PwGroup _pwGroup;
private bool _isLeftPaneOpen;
private readonly App _app = (App)Application.Current;
private bool _isEditMode;
public GroupVm() {}
public GroupVm(PwGroup pwGroup, GroupVm parent)
public GroupVm(PwGroup pwGroup, GroupVm parent, PwUuid recycleBinId = null)
{
_pwGroup = pwGroup;
ParentGroup = parent;
if (recycleBinId != null && _pwGroup.Uuid.Equals(recycleBinId)) _app.Database.RecycleBin = this;
Entries = new ObservableCollection<EntryVm>(pwGroup.Entries.Select(e => new EntryVm(e, this)).OrderBy(e => e.Name));
Entries.Insert(0, new EntryVm ());
Groups = new ObservableCollection<GroupVm>(pwGroup.Groups.Select(g => new GroupVm(g, this)).OrderBy(g => g.Name));
Groups = new ObservableCollection<GroupVm>(pwGroup.Groups.Select(g => new GroupVm(g, this, recycleBinId)).OrderBy(g => g.Name));
Groups.Insert(0, new GroupVm ());
}
@@ -94,24 +102,31 @@ namespace ModernKeePass.ViewModels
public void MarkForDelete()
{
var app = (App)Application.Current;
app.PendingDeleteEntities.Add(Id, this);
_app.PendingDeleteEntities.Add(Id, this);
ParentGroup.Groups.Remove(this);
if (_app.Database.RecycleBinEnabled && !IsSelected) _app.Database.RecycleBin.Groups.Add(this);
}
public void CommitDelete()
{
_pwGroup.ParentGroup.Groups.Remove(_pwGroup);
if (_app.Database.RecycleBinEnabled && !IsSelected) _app.Database.RecycleBin._pwGroup.AddGroup(_pwGroup, true);
}
public void UndoDelete()
{
ParentGroup.Groups.Add(this);
if (_app.Database.RecycleBinEnabled && !IsSelected) _app.Database.RecycleBin.Groups.Remove(this);
}
public void Save()
{
var app = (App)Application.Current;
app.Database.Save();
_app.Database.Save();
}
public override string ToString()
{
return Name;
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
namespace ModernKeePass.ViewModels
{
public class ListMenuItemVm : NotifyPropertyChangedBase, IIsEnabled, ISelectableModel
{
private bool _isSelected;
public string Title { get; set; }
public int Group { get; set; } = 0;
public Type PageType { get; set; }
public Symbol SymbolIcon { get; set; }
public bool IsEnabled { get; set; } = true;
public bool IsSelected
{
get { return _isSelected; }
set { SetProperty(ref _isSelected, value); }
}
public override string ToString()
{
return Title;
}
}
}

View File

@@ -1,32 +1,10 @@
using System;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using Windows.UI.Xaml.Controls;
namespace ModernKeePass.ViewModels
{
public class MainMenuItemVm: NotifyPropertyChangedBase, IIsEnabled, ISelectableModel
public class MainMenuItemVm: ListMenuItemVm
{
private bool _isSelected;
public string Title { get; set; }
public Type PageType { get; set; }
public object Parameter { get; set; }
public Frame Destination { get; set; }
public int Group { get; set; } = 0;
public Symbol SymbolIcon { get; set; }
public bool IsEnabled { get; set; } = true;
public bool IsSelected
{
get { return _isSelected; }
set { SetProperty(ref _isSelected, value); }
}
public override string ToString()
{
return Title;
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using Windows.Storage;
using Windows.Storage;
using ModernKeePass.Common;
using Windows.Storage.AccessCache;

View File

@@ -0,0 +1,45 @@
using System.Collections.ObjectModel;
using System.Linq;
using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.Pages;
namespace ModernKeePass.ViewModels
{
public class SettingsVM : NotifyPropertyChangedBase, IHasSelectableObject
{
private ListMenuItemVm _selectedItem;
public ObservableCollection<ListMenuItemVm> MenuItems { get; set; }
public ISelectableModel SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem == value) return;
if (_selectedItem != null)
{
_selectedItem.IsSelected = false;
}
SetProperty(ref _selectedItem, (ListMenuItemVm)value);
if (_selectedItem != null)
{
_selectedItem.IsSelected = true;
}
}
}
public SettingsVM()
{
MenuItems = new ObservableCollection<ListMenuItemVm>
{
new ListMenuItemVm { Title = "Database", SymbolIcon = Symbol.Setting, PageType = typeof(SettingsDatabasePage), IsSelected = true },
//new ListMenuItemVm { Title = "General", SymbolIcon = Symbol.Edit, PageType = typeof(SettingsGeneralPage) }
};
SelectedItem = MenuItems.FirstOrDefault(m => m.IsSelected);
}
}
}

View File

@@ -7,7 +7,6 @@ using Windows.UI.Xaml.Controls;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
using ModernKeePass.Pages;
using ModernKeePass.Pages.BasePages;
namespace ModernKeePass.ViewModels
{

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using Windows.Storage;
using Windows.UI.Xaml;
using ModernKeePass.Common;
using ModernKeePass.Interfaces;
namespace ModernKeePass.ViewModels
{
public class SettingsDatabaseVm: NotifyPropertyChangedBase, IHasSelectableObject
{
private readonly App _app = (App)Application.Current;
private readonly ApplicationDataContainer _localSettings = ApplicationData.Current.LocalSettings;
private GroupVm _selectedItem;
public bool HasRecycleBin
{
get { return _app.Database.RecycleBinEnabled; }
set
{
_app.Database.RecycleBinEnabled = value;
OnPropertyChanged();
}
}
public ObservableCollection<GroupVm> Groups { get; set; }
public ISelectableModel SelectedItem
{
get { return Groups.FirstOrDefault(g => g.IsSelected); }
set
{
if (_selectedItem == value) return;
if (_selectedItem != null)
{
_selectedItem.IsSelected = false;
}
SetProperty(ref _selectedItem, (GroupVm)value);
if (_selectedItem != null)
{
_selectedItem.IsSelected = true;
}
}
}
public SettingsDatabaseVm()
{
Groups = _app.Database.RootGroup.Groups;
}
// TODO: Move to another setting class (or a static class)
private T GetSetting<T>(string property)
{
try
{
return (T) Convert.ChangeType(_localSettings.Values[property], typeof(T));
}
catch (InvalidCastException)
{
return default(T);
}
}
}
}