Improve sorting

This commit is contained in:
Michael Bucari-Tovo 2022-07-11 19:07:20 -06:00
parent df5293ce1e
commit 3b42b52ff4
19 changed files with 852 additions and 411 deletions

View File

@ -11,6 +11,6 @@
<FluentTheme Mode="Light"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseLight.xaml"/>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
<StyleInclude Source="/AvaloniaUI/Assets/DataGridTheme.xaml"/>
</Application.Styles>
</Application>

View File

@ -16,10 +16,9 @@ namespace LibationWinForms.AvaloniaUI
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
};
var mainWindow = new MainWindow();
desktop.MainWindow = mainWindow;
mainWindow.OnLoad();
}
base.OnFrameworkInitializationCompleted();

View File

@ -0,0 +1,658 @@
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Styles.Resources>
<x:Double x:Key="ListAccentLowOpacity">0.6</x:Double>
<x:Double x:Key="ListAccentMediumOpacity">0.8</x:Double>
<Thickness x:Key="DataGridTextColumnCellTextBlockMargin">12,0,12,0</Thickness>
<StreamGeometry x:Key="DataGridSortIconDescendingPath">M1875 1011l-787 787v-1798h-128v1798l-787 -787l-90 90l941 941l941 -941z</StreamGeometry>
<StreamGeometry x:Key="DataGridRowGroupHeaderIconClosedPath">M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z</StreamGeometry>
<StreamGeometry x:Key="DataGridRowGroupHeaderIconOpenedPath">M1939 1581l90 -90l-1005 -1005l-1005 1005l90 90l915 -915z</StreamGeometry>
<SolidColorBrush x:Key="DataGridColumnHeaderForegroundBrush" Color="{DynamicResource SystemBaseMediumColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{DynamicResource SystemAltHighColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
<SolidColorBrush x:Key="DataGridColumnHeaderDraggedBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderBackgroundBrush" Color="{DynamicResource SystemChromeMediumColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderForegroundBrush" Color="{DynamicResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
<StaticResource x:Key="DataGridRowBackgroundBrush" ResourceKey="SystemControlTransparentBrush" />
<SolidColorBrush x:Key="DataGridRowSelectedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
<StaticResource x:Key="DataGridRowSelectedBackgroundOpacity" ResourceKey="ListAccentLowOpacity" />
<SolidColorBrush x:Key="DataGridRowSelectedHoveredBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
<StaticResource x:Key="DataGridRowSelectedHoveredBackgroundOpacity" ResourceKey="ListAccentMediumOpacity" />
<SolidColorBrush x:Key="DataGridRowSelectedUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
<StaticResource x:Key="DataGridRowSelectedUnfocusedBackgroundOpacity" ResourceKey="ListAccentLowOpacity" />
<SolidColorBrush x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
<StaticResource x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundOpacity" ResourceKey="ListAccentMediumOpacity" />
<SolidColorBrush x:Key="DataGridRowHoveredBackgroundColor" Color="{DynamicResource SystemListLowColor}" />
<SolidColorBrush x:Key="DataGridRowInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
<SolidColorBrush x:Key="DataGridRowHeaderForegroundBrush" Color="{DynamicResource SystemBaseMediumColor}" />
<SolidColorBrush x:Key="DataGridRowHeaderBackgroundBrush" Color="{DynamicResource SystemAltHighColor}" />
<StaticResource x:Key="DataGridCellBackgroundBrush" ResourceKey="SystemControlTransparentBrush" />
<SolidColorBrush x:Key="DataGridCellFocusVisualPrimaryBrush" Color="{DynamicResource SystemBaseHighColor}" />
<SolidColorBrush x:Key="DataGridCellFocusVisualSecondaryBrush" Color="{DynamicResource SystemAltMediumColor}" />
<SolidColorBrush x:Key="DataGridCellInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
<SolidColorBrush x:Key="DataGridGridLinesBrush"
Opacity="0.4"
Color="{DynamicResource SystemBaseMediumLowColor}" />
<StaticResource x:Key="DataGridCurrencyVisualPrimaryBrush" ResourceKey="SystemControlTransparentBrush" />
<SolidColorBrush x:Key="DataGridDetailsPresenterBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
<StaticResource x:Key="DataGridFillerColumnGridLinesBrush" ResourceKey="SystemControlTransparentBrush" />
</Styles.Resources>
<Style Selector="DataGridCell">
<Setter Property="Background" Value="{DynamicResource DataGridCellBackgroundBrush}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="FontSize" Value="12" />
<Setter Property="MinHeight" Value="32" />
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<ControlTemplate>
<Border x:Name="CellBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid x:Name="PART_CellRoot" ColumnDefinitions="*,Auto">
<Rectangle x:Name="CurrencyVisual"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Transparent"
IsHitTestVisible="False"
Stroke="{DynamicResource DataGridCurrencyVisualPrimaryBrush}"
StrokeThickness="1" />
<Grid x:Name="FocusVisual" IsHitTestVisible="False">
<Rectangle HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Transparent"
IsHitTestVisible="False"
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
StrokeThickness="2" />
<Rectangle Margin="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Transparent"
IsHitTestVisible="False"
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
StrokeThickness="1" />
</Grid>
<ContentPresenter Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
/>
<Rectangle x:Name="InvalidVisualElement"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
IsHitTestVisible="False"
Stroke="{DynamicResource DataGridCellInvalidBrush}"
StrokeThickness="1" />
<Rectangle Name="PART_RightGridLine"
Grid.Column="1"
Width="1"
VerticalAlignment="Stretch"
Fill="{DynamicResource DataGridFillerColumnGridLinesBrush}" />
</Grid>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="DataGridCell > TextBlock#CellTextBlock">
<Setter Property="Margin" Value="{DynamicResource DataGridTextColumnCellTextBlockMargin}" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style Selector="DataGridCell /template/ Rectangle#CurrencyVisual">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="DataGridCell /template/ Grid#FocusVisual">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="DataGridCell:current /template/ Rectangle#CurrencyVisual">
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="DataGrid:focus DataGridCell:current /template/ Grid#FocusVisual">
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="DataGridCell /template/ Rectangle#InvalidVisualElement">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="DataGridCell:invalid /template/ Rectangle#InvalidVisualElement">
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="DataGridCell > TextBox DataValidationErrors">
<Setter Property="Template" Value="{DynamicResource TooltipDataValidationContentTemplate}" />
<Setter Property="ErrorTemplate" Value="{DynamicResource TooltipDataValidationErrorTemplate}" />
</Style>
<Style Selector="DataGridColumnHeader">
<Setter Property="Foreground" Value="{DynamicResource DataGridColumnHeaderForegroundBrush}" />
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderBackgroundBrush}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Focusable" Value="False" />
<Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
<Setter Property="Padding" Value="6,0,0,0" />
<Setter Property="FontSize" Value="12" />
<Setter Property="MinHeight" Value="40" />
<Setter Property="Template">
<ControlTemplate>
<Border x:Name="HeaderBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid Name="PART_ColumnHeaderRoot" ColumnDefinitions="*,Auto">
<Grid Grid.Column="0" Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" ColumnDefinitions="*,12">
<ContentPresenter Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
<Path Name="SortIcon"
Grid.Column="1"
Height="12"
Width="8"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Fill="{TemplateBinding Foreground}"
Stretch="Uniform"
Margin="0,0,4,0"
Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z "/>
</Grid>
<Rectangle Name="VerticalSeparator"
Grid.Column="1"
Width="1"
VerticalAlignment="Stretch"
Fill="{TemplateBinding SeparatorBrush}"
IsVisible="{TemplateBinding AreSeparatorsVisible}" />
<Grid x:Name="FocusVisual" IsHitTestVisible="False">
<Rectangle x:Name="FocusVisualPrimary"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Transparent"
IsHitTestVisible="False"
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
StrokeThickness="2" />
<Rectangle x:Name="FocusVisualSecondary"
Margin="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Transparent"
IsHitTestVisible="False"
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
StrokeThickness="1" />
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="DataGridColumnHeader /template/ Grid#FocusVisual">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="DataGridColumnHeader:focus-visible /template/ Grid#FocusVisual">
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="DataGridColumnHeader:pointerover /template/ Grid#PART_ColumnHeaderRoot">
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderHoveredBackgroundBrush}" />
</Style>
<Style Selector="DataGridColumnHeader:pressed /template/ Grid#PART_ColumnHeaderRoot">
<Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderPressedBackgroundBrush}" />
</Style>
<Style Selector="DataGridColumnHeader:dragIndicator">
<Setter Property="Opacity" Value="0.5" />
</Style>
<Style Selector="DataGridColumnHeader /template/ Path#SortIcon">
<Setter Property="IsVisible" Value="False" />
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="0.9" ScaleY="0.9" />
</Setter.Value>
</Setter>
</Style>
<Style Selector="DataGridColumnHeader:sortascending /template/ Path#SortIcon">
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="DataGridColumnHeader:sortdescending /template/ Path#SortIcon">
<Setter Property="IsVisible" Value="True" />
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="0.9" ScaleY="-0.9" />
</Setter.Value>
</Setter>
</Style>
<Style Selector="DataGridRow">
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<ControlTemplate>
<Border x:Name="RowBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<DataGridFrozenGrid Name="PART_Root"
ColumnDefinitions="Auto,*"
RowDefinitions="*,Auto,Auto">
<Rectangle Name="BackgroundRectangle"
Grid.RowSpan="2"
Grid.ColumnSpan="2" />
<Rectangle x:Name="InvalidVisualElement"
Grid.ColumnSpan="2"
Fill="{DynamicResource DataGridRowInvalidBrush}" />
<DataGridRowHeader Name="PART_RowHeader"
Grid.RowSpan="3"
DataGridFrozenGrid.IsFrozen="True" />
<DataGridCellsPresenter Name="PART_CellsPresenter"
Grid.Column="1"
DataGridFrozenGrid.IsFrozen="True" />
<DataGridDetailsPresenter Name="PART_DetailsPresenter"
Grid.Row="1"
Grid.Column="1"
Background="{DynamicResource DataGridDetailsPresenterBackgroundBrush}" />
<Rectangle Name="PART_BottomGridLine"
Grid.Row="2"
Grid.Column="1"
Height="1"
HorizontalAlignment="Stretch" />
</DataGridFrozenGrid>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="DataGridRow">
<Setter Property="Background" Value="{Binding $parent[DataGrid].RowBackground}" />
</Style>
<Style Selector="DataGridRow:nth-child(even)">
<Setter Property="Background" Value="{Binding $parent[DataGrid].AlternatingRowBackground}" />
</Style>
<Style Selector="DataGridRow /template/ Rectangle#InvalidVisualElement">
<Setter Property="Opacity" Value="0" />
</Style>
<Style Selector="DataGridRow:invalid /template/ Rectangle#InvalidVisualElement">
<Setter Property="Opacity" Value="0.4" />
</Style>
<Style Selector="DataGridRow:invalid /template/ Rectangle#BackgroundRectangle">
<Setter Property="Opacity" Value="0" />
</Style>
<Style Selector="DataGridRow /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowBackgroundBrush}" />
</Style>
<Style Selector="DataGridRow:pointerover /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowHoveredBackgroundColor}" />
</Style>
<Style Selector="DataGridRow:selected /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundBrush}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" />
</Style>
<Style Selector="DataGridRow:selected:pointerover /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundBrush}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" />
</Style>
<Style Selector="DataGridRow:selected:focus /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedBackgroundBrush}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" />
</Style>
<Style Selector="DataGridRow:selected:pointerover:focus /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundBrush}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" />
</Style>
<Style Selector="DataGridRowHeader">
<Setter Property="Foreground" Value="{DynamicResource DataGridRowHeaderForegroundBrush}" />
<Setter Property="Background" Value="{DynamicResource DataGridRowHeaderBackgroundBrush}" />
<Setter Property="Focusable" Value="False" />
<Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
<Setter Property="AreSeparatorsVisible" Value="False" />
<Setter Property="Template">
<ControlTemplate>
<Grid x:Name="PART_Root"
RowDefinitions="*,*,Auto"
ColumnDefinitions="Auto,*">
<Border Grid.RowSpan="3"
Grid.ColumnSpan="2"
BorderBrush="{TemplateBinding SeparatorBrush}"
BorderThickness="0,0,1,0">
<Grid Background="{TemplateBinding Background}">
<Rectangle x:Name="RowInvalidVisualElement"
Fill="{DynamicResource DataGridRowInvalidBrush}"
Stretch="Fill" />
<Rectangle x:Name="BackgroundRectangle"
Stretch="Fill" />
</Grid>
</Border>
<Rectangle x:Name="HorizontalSeparator"
Grid.Row="2"
Grid.ColumnSpan="2"
Height="1"
Margin="1,0,1,0"
HorizontalAlignment="Stretch"
Fill="{TemplateBinding SeparatorBrush}"
IsVisible="{TemplateBinding AreSeparatorsVisible}" />
<ContentPresenter Grid.RowSpan="2"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="DataGridRowHeader /template/ Rectangle#RowInvalidVisualElement">
<Setter Property="Opacity" Value="0" />
</Style>
<Style Selector="DataGridRowHeader:invalid /template/ Rectangle#RowInvalidVisualElement">
<Setter Property="Opacity" Value="0.4" />
</Style>
<Style Selector="DataGridRowHeader:invalid /template/ Rectangle#BackgroundRectangle">
<Setter Property="Opacity" Value="0" />
</Style>
<Style Selector="DataGridRowHeader /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowBackgroundBrush}" />
</Style>
<Style Selector="DataGridRow:pointerover DataGridRowHeader /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowHoveredBackgroundColor}" />
</Style>
<Style Selector="DataGridRowHeader:selected /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundBrush}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" />
</Style>
<Style Selector="DataGridRow:pointerover DataGridRowHeader:selected /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundBrush}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" />
</Style>
<Style Selector="DataGridRowHeader:selected:focus /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedBackgroundBrush}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" />
</Style>
<Style Selector="DataGridRow:pointerover DataGridRowHeader:selected:focus /template/ Rectangle#BackgroundRectangle">
<Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundBrush}" />
<Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" />
</Style>
<Style Selector="DataGridRowGroupHeader">
<Setter Property="Focusable" Value="False" />
<Setter Property="Foreground" Value="{DynamicResource DataGridRowGroupHeaderForegroundBrush}" />
<Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderBackgroundBrush}" />
<Setter Property="FontSize" Value="15" />
<Setter Property="MinHeight" Value="32" />
<Setter Property="Template">
<ControlTemplate>
<DataGridFrozenGrid Name="PART_Root"
MinHeight="{TemplateBinding MinHeight}"
ColumnDefinitions="Auto,Auto,Auto,Auto,*"
RowDefinitions="*,Auto">
<Rectangle Name="IndentSpacer"
Grid.Column="1" />
<ToggleButton Name="ExpanderButton"
Grid.Column="2"
Width="12"
Height="12"
Margin="12,0,0,0"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
CornerRadius="{TemplateBinding CornerRadius}"
Focusable="False"
Foreground="{TemplateBinding Foreground}" />
<StackPanel Grid.Column="3"
Orientation="Horizontal"
VerticalAlignment="Center"
Margin="12,0,0,0">
<TextBlock Name="PropertyNameElement"
Margin="4,0,0,0"
IsVisible="{TemplateBinding IsPropertyNameVisible}"
Foreground="{TemplateBinding Foreground}" />
<TextBlock Margin="4,0,0,0"
Text="{Binding Key}"
Foreground="{TemplateBinding Foreground}" />
<TextBlock Name="ItemCountElement"
Margin="4,0,0,0"
IsVisible="{TemplateBinding IsItemCountVisible}"
Foreground="{TemplateBinding Foreground}" />
</StackPanel>
<Rectangle x:Name="CurrencyVisual"
Grid.ColumnSpan="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Transparent"
IsHitTestVisible="False"
Stroke="{DynamicResource DataGridCurrencyVisualPrimaryBrush}"
StrokeThickness="1" />
<Grid x:Name="FocusVisual"
Grid.ColumnSpan="5"
IsHitTestVisible="False">
<Rectangle HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Transparent"
IsHitTestVisible="False"
Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
StrokeThickness="2" />
<Rectangle Margin="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Fill="Transparent"
IsHitTestVisible="False"
Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
StrokeThickness="1" />
</Grid>
<DataGridRowHeader Name="PART_RowHeader"
Grid.RowSpan="2"
DataGridFrozenGrid.IsFrozen="True" />
<Rectangle x:Name="PART_BottomGridLine"
Grid.Row="1"
Grid.ColumnSpan="5"
Height="1" />
</DataGridFrozenGrid>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="DataGridRowGroupHeader /template/ ToggleButton#ExpanderButton">
<Setter Property="Template">
<ControlTemplate>
<Border Grid.Column="0"
Width="12"
Height="12"
Background="Transparent"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Path Fill="{TemplateBinding Foreground}"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Stretch="Uniform" />
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="DataGridRowGroupHeader /template/ ToggleButton#ExpanderButton /template/ Path">
<Setter Property="Data" Value="{StaticResource DataGridRowGroupHeaderIconOpenedPath}" />
<Setter Property="Stretch" Value="Uniform" />
</Style>
<Style Selector="DataGridRowGroupHeader /template/ ToggleButton#ExpanderButton:checked /template/ Path">
<Setter Property="Data" Value="{StaticResource DataGridRowGroupHeaderIconClosedPath}" />
<Setter Property="Stretch" Value="UniformToFill" />
</Style>
<Style Selector="DataGridRowGroupHeader /template/ DataGridFrozenGrid#PART_Root">
<Setter Property="Background" Value="{Binding $parent[DataGridRowGroupHeader].Background}" />
</Style>
<Style Selector="DataGridRowGroupHeader:pointerover /template/ DataGridFrozenGrid#PART_Root">
<Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderHoveredBackgroundBrush}" />
</Style>
<Style Selector="DataGridRowGroupHeader:pressed /template/ DataGridFrozenGrid#PART_Root">
<Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderPressedBackgroundBrush}" />
</Style>
<Style Selector="DataGridRowGroupHeader /template/ Rectangle#CurrencyVisual">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="DataGridRowGroupHeader /template/ Grid#FocusVisual">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="DataGridRowGroupHeader:current /template/ Rectangle#CurrencyVisual">
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="DataGrid:focus DataGridRowGroupHeader:current /template/ Grid#FocusVisual">
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="DataGrid">
<Setter Property="RowBackground" Value="Transparent" />
<Setter Property="AlternatingRowBackground" Value="Transparent" />
<Setter Property="HeadersVisibility" Value="Column" />
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="SelectionMode" Value="Extended" />
<Setter Property="GridLinesVisibility" Value="None" />
<Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
<Setter Property="VerticalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
<Setter Property="DropLocationIndicatorTemplate">
<Template>
<Rectangle Fill="{DynamicResource DataGridDropLocationIndicatorBackground}"
Width="2" />
</Template>
</Setter>
<Setter Property="Template">
<ControlTemplate>
<Border x:Name="DataGridBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Grid ColumnDefinitions="Auto,*,Auto" RowDefinitions="Auto,*,Auto,Auto">
<Grid.Resources>
<ControlTemplate x:Key="TopLeftHeaderTemplate"
TargetType="DataGridColumnHeader">
<Grid x:Name="TopLeftHeaderRoot"
RowDefinitions="*,*,Auto">
<Border Grid.RowSpan="2"
BorderThickness="0,0,1,0"
BorderBrush="{DynamicResource DataGridGridLinesBrush}" />
<Rectangle Grid.RowSpan="2"
VerticalAlignment="Bottom"
StrokeThickness="1"
Height="1"
Fill="{DynamicResource DataGridGridLinesBrush}" />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="TopRightHeaderTemplate"
TargetType="DataGridColumnHeader">
<Grid x:Name="RootElement" />
</ControlTemplate>
</Grid.Resources>
<DataGridColumnHeader Name="PART_TopLeftCornerHeader"
Template="{StaticResource TopLeftHeaderTemplate}" />
<DataGridColumnHeadersPresenter Name="PART_ColumnHeadersPresenter"
Grid.Column="1"
Grid.ColumnSpan="2" />
<!--<DataGridColumnHeader Name="PART_TopRightCornerHeader"
Grid.Column="2"
Template="{StaticResource TopRightHeaderTemplate}" />-->
<Rectangle Name="PART_ColumnHeadersAndRowsSeparator"
Grid.ColumnSpan="3"
VerticalAlignment="Bottom"
Height="1"
Fill="{DynamicResource DataGridGridLinesBrush}" />
<DataGridRowsPresenter Name="PART_RowsPresenter"
Grid.Row="1"
Grid.RowSpan="2"
Grid.ColumnSpan="3">
<DataGridRowsPresenter.GestureRecognizers>
<ScrollGestureRecognizer CanHorizontallyScroll="True" CanVerticallyScroll="True" />
</DataGridRowsPresenter.GestureRecognizers>
</DataGridRowsPresenter>
<Rectangle Name="PART_BottomRightCorner"
Fill="{DynamicResource DataGridScrollBarsSeparatorBackground}"
Grid.Column="2"
Grid.Row="2" />
<!--<Rectangle Name="BottomLeftCorner"
Fill="{DynamicResource DataGridScrollBarsSeparatorBackground}"
Grid.Row="2"
Grid.ColumnSpan="2" />-->
<ScrollBar Name="PART_VerticalScrollbar"
Orientation="Vertical"
Grid.Column="2"
Grid.Row="1"
Width="{DynamicResource ScrollBarSize}" />
<Grid Grid.Column="1"
Grid.Row="2"
ColumnDefinitions="Auto,*">
<Rectangle Name="PART_FrozenColumnScrollBarSpacer" />
<ScrollBar Name="PART_HorizontalScrollbar"
Grid.Column="1"
Orientation="Horizontal"
Height="{DynamicResource ScrollBarSize}" />
</Grid>
<Border x:Name="PART_DisabledVisualElement"
Grid.ColumnSpan="3"
Grid.RowSpan="4"
IsHitTestVisible="False"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CornerRadius="2"
Background="{DynamicResource DataGridDisabledVisualElementBackground}"
IsVisible="{Binding !$parent[DataGrid].IsEnabled}" />
</Grid>
</Border>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="DataGrid:empty-columns /template/ DataGridColumnHeader#PART_TopLeftCornerHeader">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="DataGrid:empty-columns /template/ DataGridColumnHeadersPresenter#PART_ColumnHeadersPresenter">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="DataGrid:empty-columns /template/ Rectangle#PART_ColumnHeadersAndRowsSeparator">
<Setter Property="IsVisible" Value="False" />
</Style>
</Styles>

View File

@ -1,32 +1,35 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Styling;
using LibationWinForms.AvaloniaUI.ViewModels;
using System;
namespace LibationWinForms.AvaloniaUI.Controls
{
/// <summary> The purpose of this extension is to immediately commit any check state changes to the viewmodel </summary>
public partial class DataGridCheckBoxColumnExt : DataGridCheckBoxColumn
{
protected override object PrepareCellForEdit(IControl editingElement, RoutedEventArgs editingEventArgs)
{
return base.PrepareCellForEdit(editingElement, editingEventArgs);
}
protected override IControl GenerateEditingElementDirect(DataGridCell cell, object dataItem)
{
var ele = base.GenerateEditingElementDirect(cell, dataItem) as CheckBox;
ele.Checked += EditingElement_Checked;
ele.Unchecked += EditingElement_Checked;
ele.Indeterminate += EditingElement_Checked;
return ele;
}
private void EditingElement_Checked(object sender, RoutedEventArgs e)
{
var cbox = sender as CheckBox;
var gEntry = cbox.DataContext as GridEntry2;
gEntry.Remove = cbox.IsChecked;
if (sender is CheckBox cbox && cbox.DataContext is GridEntry2 gentry)
{
gentry.Remove = cbox.IsChecked;
FindDataGridParent(cbox)?.CommitEdit(DataGridEditingUnit.Cell, false);
}
}
DataGrid? FindDataGridParent(IControl? control)
{
if (control?.Parent is null) return null;
else if (control?.Parent is DataGrid dg) return dg;
else return FindDataGridParent(control?.Parent);
}
}
}

View File

@ -25,71 +25,45 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
*/
public class GridEntryBindingList2 : ObservableCollection<GridEntry2>
{
public GridEntryBindingList2(IEnumerable<GridEntry2> enumeration) : base(new List<GridEntry2>(enumeration))
{
foreach (var item in enumeration)
item.PropertyChanged += Item_PropertyChanged;
}
public GridEntryBindingList2(IEnumerable<GridEntry2> enumeration)
: base(new List<GridEntry2>(enumeration)) { }
public GridEntryBindingList2(List<GridEntry2> list)
: base(list) { }
public List<GridEntry2> InternalList => Items as List<GridEntry2>;
/// <returns>All items in the list, including those filtered out.</returns>
public List<GridEntry2> AllItems() => Items.Concat(FilterRemoved).ToList();
/// <summary>When true, itms will not be checked filtered by search criteria on item changed<summary>
public bool SuspendFilteringOnUpdate { get; set; }
public string Filter { get => FilterString; set => ApplyFilter(value); }
protected MemberComparer<GridEntry2> Comparer { get; } = new();
/// <summary> Items that were removed from the base list due to filtering </summary>
private readonly List<GridEntry2> FilterRemoved = new();
private string FilterString;
private SearchResultSet SearchResults;
private bool isSorted;
#region Items Management
public new void Remove(GridEntry2 entry)
{
entry.PropertyChanged -= Item_PropertyChanged;
FilterRemoved.Add(entry);
base.Remove(entry);
}
protected override void RemoveItem(int index)
{
var item = Items[index];
item.PropertyChanged -= Item_PropertyChanged;
base.RemoveItem(index);
}
protected override void ClearItems()
{
foreach (var item in Items)
item.PropertyChanged -= Item_PropertyChanged;
base.ClearItems();
}
protected override void InsertItem(int index, GridEntry2 item)
{
item.PropertyChanged += Item_PropertyChanged;
FilterRemoved.Remove(item);
base.InsertItem(index, item);
}
private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//Don't audo-sort Remove column or else Avalonia will crash.
if (isSorted && e.PropertyName == Comparer.PropertyName && e.PropertyName != nameof(GridEntry.Remove))
{
Sort();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
return;
}
}
#endregion
#region Filtering
public void ResetCollection()
=> OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
private void ApplyFilter(string filterString)
{
if (filterString != FilterString)
@ -125,18 +99,6 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
}
}
if (isSorted)
Sort();
else
{
//No user sort is applied, so do default sorting by DateAdded, descending
Comparer.PropertyName = nameof(GridEntry.DateAdded);
Comparer.Direction = ListSortDirection.Descending;
Sort();
}
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
FilterString = null;
SearchResults = null;
}
@ -182,52 +144,5 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
}
#endregion
#region Sorting
public void DoSortCore(string propertyName)
{
if (isSorted && Comparer.PropertyName == propertyName)
{
Comparer.Direction = ~Comparer.Direction & ListSortDirection.Descending;
}
else
{
Comparer.PropertyName = propertyName;
Comparer.Direction = ListSortDirection.Descending;
}
Sort();
isSorted = true;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected void Sort()
{
var itemsList = (List<GridEntry2>)Items;
var children = itemsList.BookEntries().Where(i => i.Parent is not null).ToList();
var sortedItems = itemsList.Except(children).OrderBy(ge => ge, Comparer).ToList();
itemsList.Clear();
//Only add parentless items at this stage. After these items are added in the
//correct sorting order, go back and add the children beneath their parents.
itemsList.AddRange(sortedItems);
foreach (var parent in children.Select(c => c.Parent).Distinct())
{
var pIndex = itemsList.IndexOf(parent);
//children should always be sorted by series index.
foreach (var c in children.Where(c => c.Parent == parent).OrderBy(c => c.SeriesIndex))
itemsList.Insert(++pIndex, c);
}
}
#endregion
}
}

View File

@ -1,160 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using Avalonia.Media;
using ReactiveUI;
namespace LibationWinForms.AvaloniaUI.ViewModels
{
public class ProcessQueueItems : ObservableCollection<ItemsRepeaterPageViewModel.Item>
{
public ProcessQueueItems(IEnumerable<ItemsRepeaterPageViewModel.Item> items) :base(items) { }
public void MoveFirst(ItemsRepeaterPageViewModel.Item item)
{
var index = Items.IndexOf(item);
if (index < 1) return;
Move(index, 0);
}
public void MoveUp(ItemsRepeaterPageViewModel.Item item)
{
var index = Items.IndexOf(item);
if (index < 1) return;
Move(index, index - 1);
}
public void MoveDown(ItemsRepeaterPageViewModel.Item item)
{
var index = Items.IndexOf(item);
if (index < 0 || index > Items.Count - 2) return;
Move(index, index + 1);
}
public void MoveLast(ItemsRepeaterPageViewModel.Item item)
{
var index = Items.IndexOf(item);
if (index < 0 || index > Items.Count - 2) return;
Move(index, Items.Count - 1);
}
}
public class ItemsRepeaterPageViewModel : ViewModelBase
{
private int _newItemIndex = 1;
private int _newGenerationIndex = 0;
private ProcessQueueItems _items;
public ItemsRepeaterPageViewModel()
{
_items = CreateItems();
}
public ProcessQueueItems Items
{
get => _items;
set => this.RaiseAndSetIfChanged(ref _items, value);
}
public Item? SelectedItem { get; set; }
public void AddItem()
{
var index = SelectedItem != null ? Items.IndexOf(SelectedItem) : -1;
Items.Insert(index + 1, new Item(index + 1, $"New Item {_newItemIndex++}"));
}
public void RemoveItem()
{
if (SelectedItem is not null)
{
Items.Remove(SelectedItem);
SelectedItem = null;
}
else if (Items.Count > 0)
{
Items.RemoveAt(Items.Count - 1);
}
}
public void RandomizeHeights()
{
var random = new Random();
foreach (var i in Items)
{
i.Height = random.Next(240) + 10;
}
}
public void ResetItems()
{
Items = CreateItems();
}
private ProcessQueueItems CreateItems()
{
var suffix = _newGenerationIndex == 0 ? string.Empty : $"[{_newGenerationIndex.ToString()}]";
_newGenerationIndex++;
return new ProcessQueueItems(
Enumerable.Range(1, 100).Select(i => new Item(i, $"Item {i.ToString()} {suffix}")));
}
public class Item : ViewModelBase
{
private double _height = double.NaN;
static Random rnd = new Random();
public Item(int index, string text)
{
Index = index;
Text = text;
Narrator = "Narrator " + index;
Author = "Author " + index;
Title = "Book " + index + ": This is a book title.\r\nThis is line 2 of the book title";
Progress = rnd.Next(0, 101);
ETA = "ETA: 01:14";
IsDownloading = rnd.Next(0, 2) == 0;
if (!IsDownloading)
IsFinished = rnd.Next(0, 2) == 0;
if (IsDownloading)
Title += "\r\nDOWNLOADING";
else if (IsFinished)
Title += "\r\nFINISHED";
else
Title += "\r\nQUEUED";
}
public bool IsFinished { get; }
public bool IsDownloading { get; }
public bool Queued => !IsFinished && !IsDownloading;
public int Index { get; }
public string Text { get; }
public string ETA { get; }
public string Narrator { get; }
public string Author { get; }
public string Title { get; }
public int Progress { get; }
public double Height
{
get => _height;
set => this.RaiseAndSetIfChanged(ref _height, value);
}
}
}
}

View File

@ -1,47 +0,0 @@
using ApplicationServices;
using Avalonia.Collections;
using DataLayer;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text;
namespace LibationWinForms.AvaloniaUI.ViewModels
{
public class MainWindowViewModel : ViewModelBase
{
public string Greeting => "Welcome to Avalonia!";
public GridEntryBindingList2 People { get; set; }
public MainWindowViewModel(IEnumerable<LibraryBook> dbBooks)
{
var geList = dbBooks
.Where(lb => lb.Book.IsProduct())
.Select(b => new LibraryBookEntry2(b))
.Cast<GridEntry2>()
.ToList();
var episodes = dbBooks.Where(lb => lb.Book.IsEpisodeChild());
var seriesBooks = dbBooks.Where(lb => lb.Book.IsEpisodeParent()).ToList();
foreach (var parent in seriesBooks)
{
var seriesEpisodes = episodes.FindChildren(parent);
if (!seriesEpisodes.Any()) continue;
var seriesEntry = new SeriesEntrys2(parent, seriesEpisodes);
geList.Add(seriesEntry);
geList.AddRange(seriesEntry.Children);
}
People = new GridEntryBindingList2(geList.OrderByDescending(e => e.DateAdded));
People.CollapseAll();
}
}
}

View File

@ -12,8 +12,7 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
{
public class ProductsDisplayViewModel : ViewModelBase
{
public string Greeting => "Welcome to Avalonia!";
public GridEntryBindingList2 People { get; set; }
public GridEntryBindingList2 GridEntries { get; set; }
public ProductsDisplayViewModel(IEnumerable<LibraryBook> dbBooks)
{
var geList = dbBooks
@ -38,9 +37,8 @@ namespace LibationWinForms.AvaloniaUI.ViewModels
geList.AddRange(seriesEntry.Children);
}
People = new GridEntryBindingList2(geList.OrderByDescending(e => e.DateAdded));
People.CollapseAll();
GridEntries = new GridEntryBindingList2(geList.OrderByDescending(e => e.DateAdded));
GridEntries.CollapseAll();
}
}
}

View File

@ -20,7 +20,7 @@ namespace LibationWinForms.AvaloniaUI.Views
beginPdfBackupsToolStripMenuItem.Format(0);
pdfsCountsLbl.Text = "| [Calculating backed up PDFs]";
Opened += setBackupCounts;
Load += setBackupCounts;
LibraryCommands.LibrarySizeChanged += setBackupCounts;
LibraryCommands.BookUserDefinedItemCommitted += setBackupCounts;

View File

@ -14,8 +14,8 @@ namespace LibationWinForms.AvaloniaUI.Views
{
private void Configure_QuickFilters()
{
Opened += updateFirstFilterIsDefaultToolStripMenuItem;
Opened += updateFiltersMenu;
Load += updateFirstFilterIsDefaultToolStripMenuItem;
Load += updateFiltersMenu;
QuickFilters.UseDefaultChanged += updateFirstFilterIsDefaultToolStripMenuItem;
QuickFilters.Updated += updateFiltersMenu;
}

View File

@ -1,11 +1,7 @@
using AudibleUtilities;
using Avalonia.Controls;
using LibationWinForms.Dialogs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LibationWinForms.AvaloniaUI.Views
{
@ -16,6 +12,7 @@ namespace LibationWinForms.AvaloniaUI.Views
{
removeBooksBtn.IsVisible = false;
doneRemovingBtn.IsVisible = false;
removeLibraryBooksToolStripMenuItem.Click += removeLibraryBooksToolStripMenuItem_Click;
}
public async void removeBooksBtn_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)

View File

@ -46,9 +46,9 @@ namespace LibationWinForms.AvaloniaUI.Views
};
// load init state to menu checkbox
Opened += updateAutoScanLibraryToolStripMenuItem;
Load += updateAutoScanLibraryToolStripMenuItem;
// if enabled: begin on load
Opened += startAutoScan;
Load += startAutoScan;
// if new 'default' account is added, run autoscan
AccountsSettingsPersister.Saving += accountsPreSave;

View File

@ -16,7 +16,7 @@ namespace LibationWinForms.AvaloniaUI.Views
{
private void Configure_ScanManual()
{
Opened += refreshImportMenu;
Load += refreshImportMenu;
AccountsSettingsPersister.Saved += refreshImportMenu;
}
@ -33,8 +33,21 @@ namespace LibationWinForms.AvaloniaUI.Views
scanLibraryOfSomeAccountsToolStripMenuItem.IsVisible = count > 1;
removeLibraryBooksToolStripMenuItem.IsVisible = count > 0;
removeSomeAccountsToolStripMenuItem.IsVisible = count > 1;
removeAllAccountsToolStripMenuItem.IsVisible = count > 1;
//Avalonia will not fire the Click event for a MenuItem with children,
//so if only 1 account, remove the children. Otherwise add children
//for multiple accounts.
removeLibraryBooksToolStripMenuItem.Items = null;
if (count > 1)
{
removeLibraryBooksToolStripMenuItem.Items =
new List<Control>
{
removeSomeAccountsToolStripMenuItem,
removeAllAccountsToolStripMenuItem
};
}
}
public void noAccountsYetAddAccountToolStripMenuItem_Click(object sender, Avalonia.Interactivity.RoutedEventArgs e)

View File

@ -90,7 +90,7 @@
<!-- Product Display Grid -->
<views:ProductsDisplay2
Initialized="productsDisplay_Initialized"
InitialLoaded="productsDisplay_Initialized"
LiberateClicked="ProductsDisplay_LiberateClicked"
RemovableCountChanged="productsDisplay_RemovableCountChanged"
VisibleCountChanged="productsDisplay_VisibleCountChanged"

View File

@ -43,58 +43,13 @@ namespace LibationWinForms.AvaloniaUI.Views
Configure_NonUI();
{
this.Load += (_, _) => productsDisplay.Display();
LibraryCommands.LibrarySizeChanged += (_, __) => Dispatcher.UIThread.Post(() => productsDisplay.Display());
}
}
/*
MenuItem importToolStripMenuItem;
MenuItem autoScanLibraryToolStripMenuItem;
CheckBox autoScanLibraryToolStripMenuItemCheckbox;
MenuItem noAccountsYetAddAccountToolStripMenuItem;
MenuItem scanLibraryToolStripMenuItem;
MenuItem scanLibraryOfAllAccountsToolStripMenuItem;
MenuItem scanLibraryOfSomeAccountsToolStripMenuItem;
MenuItem removeLibraryBooksToolStripMenuItem;
MenuItem removeAllAccountsToolStripMenuItem;
MenuItem removeSomeAccountsToolStripMenuItem;
MenuItem liberateToolStripMenuItem;
MenuItem beginBookBackupsToolStripMenuItem;
MenuItem beginPdfBackupsToolStripMenuItem;
MenuItem convertAllM4bToMp3ToolStripMenuItem;
MenuItem liberateVisibleToolStripMenuItem_LiberateMenu;
MenuItem exportToolStripMenuItem;
MenuItem exportLibraryToolStripMenuItem;
MenuItem quickFiltersToolStripMenuItem;
MenuItem firstFilterIsDefaultToolStripMenuItem;
CheckBox firstFilterIsDefaultToolStripMenuItem_Checkbox;
MenuItem editQuickFiltersToolStripMenuItem;
MenuItem visibleBooksToolStripMenuItem;
MenuItem liberateVisibleToolStripMenuItem_VisibleBooksMenu;
MenuItem replaceTagsToolStripMenuItem;
MenuItem setDownloadedToolStripMenuItem;
MenuItem removeToolStripMenuItem;
MenuItem settingsToolStripMenuItem;
MenuItem accountsToolStripMenuItem;
MenuItem basicSettingsToolStripMenuItem;
MenuItem aboutToolStripMenuItem;
public event EventHandler Load;
StackPanel scanningToolStripMenuItem;
TextBlock scanningToolStripMenuItem_Text;
Button filterHelpBtn;
Button addQuickFilterBtn;
TextBox filterSearchTb;
Button filterBtn;
Button toggleQueueHideBtn;
StackPanel removeBooksButtonsPanel;
Button removeBooksBtn;
SplitView splitContainer1;
ProductsDisplay2 productsDisplay;
ProcessQueueControl2 processBookQueue1;
*/
public void OnLoad() => Load?.Invoke(this, EventArgs.Empty);
private void FindAllControls()
{

View File

@ -4,14 +4,16 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:LibationWinForms.AvaloniaUI.Views"
xmlns:controls="clr-namespace:LibationWinForms.AvaloniaUI.Controls"
mc:Ignorable="d" d:DesignWidth="1560" d:DesignHeight="700"
mc:Ignorable="d" d:DesignWidth="1560" d:DesignHeight="400"
x:Class="LibationWinForms.AvaloniaUI.Views.ProductsDisplay2">
<Grid>
<DataGrid Name="productsGrid" AutoGenerateColumns="False" Items="{Binding People}">
<DataGrid Name="productsGrid" AutoGenerateColumns="False" Items="{Binding GridEntries}">
<DataGrid.Columns>
<controls:DataGridCheckBoxColumnExt IsVisible="True" Header="Remove" IsThreeState="True" IsReadOnly="False" CanUserSort="True" Binding="{Binding Remove, Mode=TwoWay}" Width="60" SortMemberPath="Remove"/>
<controls:DataGridCheckBoxColumnExt IsVisible="True" Header="Remove" IsThreeState="True" IsReadOnly="False" CanUserSort="True" Binding="{Binding Remove, Mode=TwoWay}" Width="70" SortMemberPath="Remove"/>
<DataGridTemplateColumn CanUserSort="True" Width="75" Header="Liberate" SortMemberPath="Liberate">
<DataGridTemplateColumn.CellTemplate>
@ -25,6 +27,7 @@
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="80" Header="Cover">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
@ -103,17 +106,17 @@
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="125" Header="Product Rating" CanUserSort="True" SortMemberPath="ProductRating">
<DataGridTemplateColumn Width="125" Header="Product&#xA;Rating" CanUserSort="True" SortMemberPath="ProductRating">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border BorderThickness="3" Height="80">
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding ProductRating}" />
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap" FontSize="11" Text="{Binding ProductRating}" />
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="100" Header="Purchase Date" CanUserSort="True" SortMemberPath="PurchaseDate">
<DataGridTemplateColumn Width="100" Header="Purchase&#xA;Date" CanUserSort="True" SortMemberPath="PurchaseDate">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border BorderThickness="3" Height="80">
@ -127,17 +130,17 @@
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border BorderThickness="3" Height="80">
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding MyRating}" />
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap" FontSize="11" Text="{Binding MyRating}" />
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="140" Header="Misc" CanUserSort="True" SortMemberPath="Misc">
<DataGridTemplateColumn Width="135" Header="Misc" CanUserSort="True" SortMemberPath="Misc">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border BorderThickness="3" Height="80">
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding Misc}" />
<TextBlock VerticalAlignment="Center" TextWrapping="WrapWithOverflow" FontSize="10" Text="{Binding Misc}" />
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>

View File

@ -4,11 +4,14 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using DataLayer;
using Dinah.Core.DataBinding;
using FileLiberator;
using LibationFileManager;
using LibationWinForms.AvaloniaUI.ViewModels;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
@ -20,6 +23,7 @@ namespace LibationWinForms.AvaloniaUI.Views
public event EventHandler<int> VisibleCountChanged;
public event EventHandler<int> RemovableCountChanged;
public event EventHandler<LibraryBook> LiberateClicked;
public event EventHandler InitialLoaded;
private ProductsDisplayViewModel _viewModel;
private GridEntryBindingList2 bindingList => productsGrid.Items as GridEntryBindingList2;
@ -42,11 +46,10 @@ namespace LibationWinForms.AvaloniaUI.Views
productsGrid.CanUserSortColumns = true;
removeGVColumn = productsGrid.Columns[0];
var dbBooks = DbContexts.GetLibrary_Flat_NoTracking(includeParents: true);
productsGrid.DataContext = _viewModel = new ProductsDisplayViewModel(dbBooks);
this.AttachedToVisualTree +=(_, _) => VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count());
}
public override void EndInit()
{
base.EndInit();
}
private void InitializeComponent()
@ -54,20 +57,106 @@ namespace LibationWinForms.AvaloniaUI.Views
AvaloniaXamlLoader.Load(this);
}
private class RowComparer : IComparer
{
private static readonly System.Reflection.PropertyInfo HeaderCellPi = typeof(DataGridColumn).GetProperty("HeaderCell", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
private static readonly System.Reflection.PropertyInfo CurrentSortingStatePi = typeof(DataGridColumnHeader).GetProperty("CurrentSortingState", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
public DataGridColumn Column { get; init; }
public string PropertyName { get; init; }
public ListSortDirection? SortDirection { get; set; }
/// <summary>
/// This compare method ensures that all top-level grid entries (standalone books or series parents)
/// are sorted by PropertyName while all episodes remain immediately beneath their parents and remain
/// sorted by series index, ascending.
/// </summary>
public int Compare(object x, object y)
{
if (x is null) return -1;
if (y is null) return 1;
if (x is null && y is null) return 0;
var geA = (GridEntry2)x;
var geB = (GridEntry2)y;
SortDirection ??= GetSortOrder(Column);
SeriesEntrys2 parentA = null;
SeriesEntrys2 parentB = null;
if (geA is LibraryBookEntry2 lbA && lbA.Parent is SeriesEntrys2 seA)
parentA = seA;
if (geB is LibraryBookEntry2 lbB && lbB.Parent is SeriesEntrys2 seB)
parentB = seB;
//both a and b are standalone
if (parentA is null && parentB is null)
return Compare(geA, geB);
//a is a standalone, b is a child
if (parentA is null && parentB is not null)
{
// b is a child of a, parent is always first
if (parentB == geA)
return SortDirection is ListSortDirection.Ascending ? -1 : 1;
else
return Compare(geA, parentB);
}
//a is a child, b is a standalone
if (parentA is not null && parentB is null)
{
// a is a child of b, parent is always first
if (parentA == geB)
return SortDirection is ListSortDirection.Ascending ? 1 : -1;
else
return Compare(parentA, geB);
}
//both are children of the same series, always present in order of series index, ascending
if (parentA == parentB)
return geA.SeriesIndex.CompareTo(geB.SeriesIndex) * (SortDirection is ListSortDirection.Ascending ? 1 : -1);
//a and b are children of different series.
return Compare(parentA, parentB);
}
private static ListSortDirection? GetSortOrder(DataGridColumn column)
=> CurrentSortingStatePi.GetValue(HeaderCellPi.GetValue(column)) as ListSortDirection?;
private int Compare(GridEntry2 x, GridEntry2 y)
{
var val1 = x.GetMemberValue(PropertyName);
var val2 = y.GetMemberValue(PropertyName);
return x.GetMemberComparer(val1.GetType()).Compare(val1, val2);
}
}
Dictionary<DataGridColumn, RowComparer> ColumnComparers = new();
DataGridColumn CurrentSortColumn;
private void Dg1_Sorting(object sender, DataGridColumnEventArgs e)
{
bindingList.DoSortCore(e.Column.SortMemberPath);
e.Handled = true;
if (!ColumnComparers.ContainsKey(e.Column))
ColumnComparers[e.Column] = new RowComparer
{
Column = e.Column,
PropertyName = e.Column.SortMemberPath
};
//Force the comparer to get the current sort order. We can't
//retrieve it from inside this event handler because Avalonia
//doesn't set the property until after this event.
ColumnComparers[e.Column].SortDirection = null;
e.Column.CustomSortComparer = ColumnComparers[e.Column];
CurrentSortColumn = e.Column;
}
#region Button controls
public void Remove_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
{
productsGrid.CommitEdit(DataGridEditingUnit.Cell, true);
RemovableCountChanged?.Invoke(this, GetAllBookEntries().Count(lbe => lbe.Remove is true));
}
public void LiberateButton_Click(object sender, Avalonia.Interactivity.RoutedEventArgs args)
{
var button = args.Source as Button;
@ -228,6 +317,12 @@ namespace LibationWinForms.AvaloniaUI.Views
{
// don't return early if lib size == 0. this will not update correctly if all books are removed
var dbBooks = DbContexts.GetLibrary_Flat_NoTracking(includeParents: true);
if (productsGrid.DataContext is null)
{
productsGrid.DataContext = _viewModel = new ProductsDisplayViewModel(dbBooks);
InitialLoaded?.Invoke(this, EventArgs.Empty);
VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count());
}
UpdateGrid(dbBooks);
}
catch (Exception ex)
@ -387,6 +482,14 @@ namespace LibationWinForms.AvaloniaUI.Views
if (visibleCount != bindingList.Count)
VisibleCountChanged?.Invoke(this, bindingList.BookEntries().Count());
//Re-sort after filtering
if (CurrentSortColumn is null)
bindingList.InternalList.Sort((i1, i2) => i2.DateAdded.CompareTo(i1.DateAdded));
else
CurrentSortColumn?.Sort(ColumnComparers[CurrentSortColumn].SortDirection.Value);
bindingList.ResetCollection();
}
#endregion

View File

@ -55,6 +55,7 @@
<ItemGroup>
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.3" />
<PackageReference Include="Avalonia" Version="0.10.16" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.16" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.16" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.16" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.16" />

View File

@ -19,6 +19,8 @@ namespace LibationWinForms
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
static extern bool AllocConsole();
static bool UseAvaloniaUI = true;
[STAThread]
static void Main()
{
@ -76,9 +78,10 @@ namespace LibationWinForms
// global exception handling (ShowAdminAlert) attempts to use logging. only call it after logging has been init'd
postLoggingGlobalExceptionHandling();
if (UseAvaloniaUI)
BuildAvaloniaApp().StartWithClassicDesktopLifetime(null);
//System.Windows.Forms.Application.Run(new Form1());
else
System.Windows.Forms.Application.Run(new Form1());
}
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()