Improve sorting
This commit is contained in:
parent
df5293ce1e
commit
3b42b52ff4
@ -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>
|
||||
@ -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();
|
||||
|
||||
658
Source/LibationWinForms/AvaloniaUI/Assets/DataGridTheme.xaml
Normal file
658
Source/LibationWinForms/AvaloniaUI/Assets/DataGridTheme.xaml
Normal 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>
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -90,7 +90,7 @@
|
||||
|
||||
<!-- Product Display Grid -->
|
||||
<views:ProductsDisplay2
|
||||
Initialized="productsDisplay_Initialized"
|
||||
InitialLoaded="productsDisplay_Initialized"
|
||||
LiberateClicked="ProductsDisplay_LiberateClicked"
|
||||
RemovableCountChanged="productsDisplay_RemovableCountChanged"
|
||||
VisibleCountChanged="productsDisplay_VisibleCountChanged"
|
||||
|
||||
@ -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()
|
||||
{
|
||||
|
||||
@ -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
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
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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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>()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user