-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Add SQLite database preview support to Peek #45846
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
thomastv
wants to merge
1
commit into
microsoft:main
Choose a base branch
from
thomastv:feature/sqlite-db-previewer
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+587
−0
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
110 changes: 110 additions & 0 deletions
110
src/modules/peek/Peek.FilePreviewer/Controls/SQLiteControl.xaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| <!-- Copyright (c) Microsoft Corporation. All rights reserved. --> | ||
| <!-- Licensed under the MIT License. See LICENSE in the project root for license information. --> | ||
|
|
||
| <UserControl | ||
| x:Class="Peek.FilePreviewer.Controls.SQLiteControl" | ||
| xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
| xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
| xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
| xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
| xmlns:toolkit="using:CommunityToolkit.WinUI.UI.Controls" | ||
| mc:Ignorable="d"> | ||
|
|
||
| <Grid> | ||
| <Grid.RowDefinitions> | ||
| <RowDefinition Height="*" /> | ||
| <RowDefinition Height="Auto" /> | ||
| </Grid.RowDefinitions> | ||
|
|
||
| <Grid> | ||
| <Grid.ColumnDefinitions> | ||
| <ColumnDefinition Width="220" MinWidth="150" MaxWidth="320" /> | ||
| <ColumnDefinition Width="Auto" /> | ||
| <ColumnDefinition Width="*" /> | ||
| </Grid.ColumnDefinitions> | ||
|
|
||
| <!-- Left pane: table tree --> | ||
| <TreeView | ||
| x:Name="TableTreeView" | ||
| Grid.Column="0" | ||
| ItemInvoked="TableTreeView_ItemInvoked"> | ||
| <TreeView.ItemTemplate> | ||
| <DataTemplate> | ||
| <TextBlock Text="{Binding Content}" TextTrimming="CharacterEllipsis"> | ||
| <ToolTipService.ToolTip> | ||
| <ToolTip Content="{Binding Content}" /> | ||
| </ToolTipService.ToolTip> | ||
| </TextBlock> | ||
| </DataTemplate> | ||
| </TreeView.ItemTemplate> | ||
| </TreeView> | ||
|
|
||
| <!-- Divider --> | ||
| <Border | ||
| Grid.Column="1" | ||
| Width="1" | ||
| Background="{ThemeResource CardStrokeColorDefaultBrush}" /> | ||
|
|
||
| <!-- Right pane --> | ||
| <Grid Grid.Column="2"> | ||
| <Grid.RowDefinitions> | ||
| <RowDefinition Height="Auto" /> | ||
| <RowDefinition Height="*" /> | ||
| </Grid.RowDefinitions> | ||
|
|
||
| <!-- Record count header --> | ||
| <Border | ||
| x:Name="RecordCountHeader" | ||
| Grid.Row="0" | ||
| Padding="12,6" | ||
| BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" | ||
| BorderThickness="0,0,0,1" | ||
| Visibility="Collapsed"> | ||
| <TextBlock x:Name="RecordCountText" /> | ||
| </Border> | ||
|
|
||
| <!-- Data grid --> | ||
| <toolkit:DataGrid | ||
| x:Name="TableDataGrid" | ||
| Grid.Row="1" | ||
| AreRowDetailsFrozen="True" | ||
| AutoGenerateColumns="False" | ||
| CanDrag="False" | ||
| ColumnWidth="Auto" | ||
| GridLinesVisibility="All" | ||
| HeadersVisibility="Column" | ||
| IsReadOnly="True" | ||
| MinColumnWidth="80" | ||
| RowDetailsVisibilityMode="Collapsed" | ||
| SelectionMode="Extended" | ||
| SizeChanged="TableDataGrid_SizeChanged" | ||
| Visibility="Collapsed" /> | ||
|
|
||
| <!-- Placeholder when no table is selected --> | ||
| <TextBlock | ||
| x:Name="NoSelectionText" | ||
| x:Uid="SQLite_NoTableSelected" | ||
| Grid.Row="0" | ||
| Grid.RowSpan="2" | ||
| HorizontalAlignment="Center" | ||
| VerticalAlignment="Center" | ||
| Foreground="{ThemeResource TextFillColorSecondaryBrush}" /> | ||
| </Grid> | ||
| </Grid> | ||
|
|
||
| <!-- Footer --> | ||
| <Border | ||
| Grid.Row="1" | ||
| Margin="16" | ||
| HorizontalAlignment="Center" | ||
| Background="{ThemeResource CardBackgroundFillColorDefaultBrush}" | ||
| BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}" | ||
| BorderThickness="1" | ||
| CornerRadius="8"> | ||
| <TextBlock | ||
| Padding="16,8,16,8" | ||
| IsTextSelectionEnabled="True" | ||
| Text="{x:Bind TableCount, Mode=OneWay}" /> | ||
| </Border> | ||
| </Grid> | ||
| </UserControl> |
206 changes: 206 additions & 0 deletions
206
src/modules/peek/Peek.FilePreviewer/Controls/SQLiteControl.xaml.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| // Copyright (c) Microsoft Corporation | ||
| // The Microsoft Corporation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using System.Collections.ObjectModel; | ||
| using System.Collections.Specialized; | ||
| using System.Globalization; | ||
|
|
||
| using CommunityToolkit.WinUI.UI.Controls; | ||
| using Microsoft.UI.Dispatching; | ||
| using Microsoft.UI.Xaml; | ||
| using Microsoft.UI.Xaml.Controls; | ||
| using Microsoft.UI.Xaml.Data; | ||
| using Peek.Common.Helpers; | ||
| using Peek.FilePreviewer.Previewers; | ||
| using Peek.FilePreviewer.Previewers.SQLitePreviewer; | ||
| using Peek.FilePreviewer.Previewers.SQLitePreviewer.Models; | ||
|
|
||
| namespace Peek.FilePreviewer.Controls | ||
| { | ||
| public sealed partial class SQLiteControl : UserControl | ||
| { | ||
| public static readonly DependencyProperty TablesProperty = DependencyProperty.Register( | ||
| nameof(Tables), | ||
| typeof(ObservableCollection<SQLiteTableInfo>), | ||
| typeof(SQLitePreviewer), | ||
| new PropertyMetadata(null, OnTablesPropertyChanged)); | ||
|
|
||
| public static readonly DependencyProperty LoadingStateProperty = DependencyProperty.Register( | ||
| nameof(LoadingState), | ||
| typeof(PreviewState), | ||
| typeof(SQLitePreviewer), | ||
| new PropertyMetadata(PreviewState.Uninitialized)); | ||
|
|
||
| public static readonly DependencyProperty TableCountProperty = DependencyProperty.Register( | ||
| nameof(TableCount), | ||
| typeof(string), | ||
| typeof(SQLitePreviewer), | ||
| new PropertyMetadata(null)); | ||
|
|
||
| private double _lastColumnAutoWidth = double.NaN; | ||
|
|
||
| public ObservableCollection<SQLiteTableInfo>? Tables | ||
| { | ||
| get => (ObservableCollection<SQLiteTableInfo>?)GetValue(TablesProperty); | ||
| set => SetValue(TablesProperty, value); | ||
| } | ||
|
|
||
| public PreviewState? LoadingState | ||
| { | ||
| get => (PreviewState)GetValue(LoadingStateProperty); | ||
| set => SetValue(LoadingStateProperty, value); | ||
| } | ||
|
|
||
| public string? TableCount | ||
| { | ||
| get => (string?)GetValue(TableCountProperty); | ||
| set => SetValue(TableCountProperty, value); | ||
| } | ||
|
|
||
| public SQLiteControl() | ||
| { | ||
| this.InitializeComponent(); | ||
| } | ||
|
|
||
| private static void OnTablesPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) | ||
| { | ||
| var control = (SQLiteControl)d; | ||
|
|
||
| if (e.OldValue is ObservableCollection<SQLiteTableInfo> oldCollection) | ||
| { | ||
| oldCollection.CollectionChanged -= control.OnTablesCollectionChanged; | ||
| } | ||
|
|
||
| control.TableTreeView.RootNodes.Clear(); | ||
| control.ClearDataView(); | ||
|
|
||
| if (e.NewValue is ObservableCollection<SQLiteTableInfo> newCollection) | ||
| { | ||
| newCollection.CollectionChanged += control.OnTablesCollectionChanged; | ||
| foreach (var table in newCollection) | ||
| { | ||
| control.AddTableNode(table); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private void OnTablesCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) | ||
| { | ||
| if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null) | ||
| { | ||
| foreach (SQLiteTableInfo table in e.NewItems) | ||
| { | ||
| AddTableNode(table); | ||
| } | ||
| } | ||
| else if (e.Action == NotifyCollectionChangedAction.Reset) | ||
| { | ||
| TableTreeView.RootNodes.Clear(); | ||
| ClearDataView(); | ||
| } | ||
| } | ||
|
|
||
| private void AddTableNode(SQLiteTableInfo table) | ||
| { | ||
| var tableNode = new TreeViewNode { Content = table }; | ||
| foreach (var col in table.Columns) | ||
| { | ||
| tableNode.Children.Add(new TreeViewNode { Content = col.DisplayText }); | ||
| } | ||
|
|
||
| TableTreeView.RootNodes.Add(tableNode); | ||
| } | ||
|
|
||
| private void TableTreeView_ItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args) | ||
| { | ||
| if (args.InvokedItem is TreeViewNode { Content: SQLiteTableInfo table }) | ||
| { | ||
| ShowTableData(table); | ||
| } | ||
| } | ||
|
|
||
| private void ShowTableData(SQLiteTableInfo table) | ||
| { | ||
| _lastColumnAutoWidth = double.NaN; | ||
| TableDataGrid.Columns.Clear(); | ||
| foreach (var col in table.Columns) | ||
| { | ||
| TableDataGrid.Columns.Add(new DataGridTextColumn | ||
| { | ||
| Header = col.Name, | ||
| Binding = new Binding { Path = new PropertyPath($"[{col.Name}]") }, | ||
| IsReadOnly = true, | ||
| }); | ||
| } | ||
|
|
||
| TableDataGrid.ItemsSource = table.Rows; | ||
|
|
||
| // After columns and rows are set, defer measurement until layout has completed | ||
| // so ActualWidth values are valid when we decide whether to stretch the last column. | ||
| TableDataGrid.DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, AdjustLastColumnWidth); | ||
|
|
||
| RecordCountText.Text = string.Format( | ||
| CultureInfo.CurrentCulture, | ||
| ResourceLoaderInstance.ResourceLoader.GetString("SQLite_Row_Count"), | ||
| table.RowCount); | ||
|
|
||
| RecordCountHeader.Visibility = Visibility.Visible; | ||
| TableDataGrid.Visibility = Visibility.Visible; | ||
| NoSelectionText.Visibility = Visibility.Collapsed; | ||
| } | ||
|
|
||
| private void TableDataGrid_SizeChanged(object sender, SizeChangedEventArgs e) | ||
| { | ||
| AdjustLastColumnWidth(); | ||
| } | ||
|
|
||
| private void AdjustLastColumnWidth() | ||
| { | ||
| if (TableDataGrid.Columns.Count == 0 || TableDataGrid.ActualWidth <= 0) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var lastCol = TableDataGrid.Columns[TableDataGrid.Columns.Count - 1]; | ||
|
|
||
| // Capture the last column's natural auto-width the first time it is measured. | ||
| // Once the column is Star-stretched we keep using the stored value so that | ||
| // window resizes can correctly revert to Auto when the grid becomes too narrow. | ||
| if (!lastCol.Width.IsStar && lastCol.ActualWidth > 0) | ||
| { | ||
| _lastColumnAutoWidth = lastCol.ActualWidth; | ||
| } | ||
|
|
||
| if (double.IsNaN(_lastColumnAutoWidth) || _lastColumnAutoWidth <= 0) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| double otherColumnsWidth = 0; | ||
| for (int i = 0; i < TableDataGrid.Columns.Count - 1; i++) | ||
| { | ||
| otherColumnsWidth += TableDataGrid.Columns[i].ActualWidth; | ||
| } | ||
|
|
||
| if (otherColumnsWidth + _lastColumnAutoWidth < TableDataGrid.ActualWidth) | ||
| { | ||
| lastCol.Width = new DataGridLength(1, DataGridLengthUnitType.Star); | ||
| } | ||
| else | ||
| { | ||
| lastCol.Width = new DataGridLength(1, DataGridLengthUnitType.Auto); | ||
| } | ||
| } | ||
|
|
||
| private void ClearDataView() | ||
| { | ||
| _lastColumnAutoWidth = double.NaN; | ||
| TableDataGrid.Columns.Clear(); | ||
| TableDataGrid.ItemsSource = null; | ||
| RecordCountHeader.Visibility = Visibility.Collapsed; | ||
| TableDataGrid.Visibility = Visibility.Collapsed; | ||
| NoSelectionText.Visibility = Visibility.Visible; | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,6 +57,7 @@ | |
| [NotifyPropertyChangedFor(nameof(AudioPreviewer))] | ||
| [NotifyPropertyChangedFor(nameof(BrowserPreviewer))] | ||
| [NotifyPropertyChangedFor(nameof(ArchivePreviewer))] | ||
| [NotifyPropertyChangedFor(nameof(SQLitePreviewer))] | ||
| [NotifyPropertyChangedFor(nameof(ShellPreviewHandlerPreviewer))] | ||
| [NotifyPropertyChangedFor(nameof(DrivePreviewer))] | ||
| [NotifyPropertyChangedFor(nameof(SpecialFolderPreviewer))] | ||
|
|
@@ -111,6 +112,8 @@ | |
|
|
||
| public IArchivePreviewer? ArchivePreviewer => Previewer as IArchivePreviewer; | ||
|
|
||
| public ISQLitePreviewer? SQLitePreviewer => Previewer as ISQLitePreviewer; | ||
Check failureCode scanning / check-spelling Unrecognized Spelling Error
ISQ is not a recognized word. (unrecognized-spelling)
|
||
|
|
||
| public IShellPreviewHandlerPreviewer? ShellPreviewHandlerPreviewer => Previewer as IShellPreviewHandlerPreviewer; | ||
|
|
||
| public IDrivePreviewer? DrivePreviewer => Previewer as IDrivePreviewer; | ||
|
|
@@ -198,6 +201,7 @@ | |
| AudioPreview.Visibility = Visibility.Collapsed; | ||
| BrowserPreview.Visibility = Visibility.Collapsed; | ||
| ArchivePreview.Visibility = Visibility.Collapsed; | ||
| SQLitePreview.Visibility = Visibility.Collapsed; | ||
| DrivePreview.Visibility = Visibility.Collapsed; | ||
| UnsupportedFilePreview.Visibility = Visibility.Collapsed; | ||
|
|
||
|
|
@@ -265,6 +269,7 @@ | |
| ImagePreview.Source = null; | ||
| ArchivePreview.Source = null; | ||
| BrowserPreview.Source = null; | ||
| SQLitePreview.Tables = null; | ||
| DrivePreview.Source = null; | ||
|
|
||
| ShellPreviewHandlerPreviewer?.Clear(); | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Check failure
Code scanning / check-spelling
Unrecognized Spelling Error