Skip to content

Commit e0efd20

Browse files
Khalil Benazzouzclaude
andcommitted
v2.1.2 — Results polish: horizontal scroll, zoom, timeout + keyword bracketing
- Query Editor: configurable CommandTimeout (default 120s, 0=unlimited), zoom -/reset/+ controls, horizontal scrolling on wide result sets, MaxHeight and SizeToHeader column width, row/column virtualization. - Autocomplete: SqlCompletionItem.Complete brackets identifiers so tables named like reserved keywords (transaction/user/order/...) parse correctly. Tables/views/procs/functions always qualified as [schema].[name]; columns bracketed only when reserved or non-identifier. - PrimaryButton: explicit Horizontal/VerticalContentAlignment=Center — fixes off-centre text on Connect/Execute/Build buttons. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8319b27 commit e0efd20

9 files changed

Lines changed: 140 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to DB TEAM are documented here. Format: [Keep a Changelog](h
44

55
## [Unreleased]
66

7+
## [2.1.2] — 2026-04-20 — Results grid polish + reserved-keyword bracketing + configurable timeout
8+
9+
### Added
10+
- **Configurable query timeout** — Query Editor result pane gains a `Timeout` box (default **120 s**, was 30 s). `0` = unlimited. Applies to the current tab only.
11+
- **Results zoom**`-` / reset / `+` buttons above the grid bind to a `ScaleTransform` so the whole result set can be shrunk to fit a wide table or enlarged for readability. Percentage is displayed.
12+
- **Horizontal scrolling** — the results host is now a `ScrollViewer` with `HorizontalScrollBarVisibility=Auto`, and each inner `DataGrid` has its own horizontal scrollbar plus `MaxHeight=500`, `ColumnWidth=SizeToHeader` and row/column virtualisation. Wide result sets no longer clip their rightmost columns off-screen.
13+
14+
### Fixed
15+
- **Reserved-keyword collisions**`SqlCompletionItem.Complete` now brackets identifiers on insertion. Tables/views/procedures/functions are always qualified as `[schema].[name]`; columns get bracketed only when they collide with a T-SQL reserved word (`transaction`, `user`, `order`, …) or contain a non-identifier character. A user table named `transaction` no longer parses as the `TRANSACTION` keyword.
16+
- **Button content alignment**`PrimaryButton` (and every style that inherits from it, e.g. `SuccessButton`, `DangerButton`) explicitly sets `HorizontalContentAlignment=Center` and `VerticalContentAlignment=Center`. The Connect/Execute/Build buttons now render their text centred when padding or layout stretched them before.
17+
18+
### Changed
19+
- Version bumped to **2.1.2**.
20+
721
## [2.1.1] — 2026-04-18 — Release CI fix + dock pane live-switch
822

923
### Fixed

Directory.Build.props

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<Project>
22

33
<PropertyGroup>
4-
<Version>2.1.1</Version>
5-
<AssemblyVersion>2.1.1.0</AssemblyVersion>
6-
<FileVersion>2.1.1.0</FileVersion>
7-
<InformationalVersion>2.1.1</InformationalVersion>
4+
<Version>2.1.2</Version>
5+
<AssemblyVersion>2.1.2.0</AssemblyVersion>
6+
<FileVersion>2.1.2.0</FileVersion>
7+
<InformationalVersion>2.1.2</InformationalVersion>
88

99
<Company>DB TEAM</Company>
1010
<Authors>Khalil Benazzouz</Authors>

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# DB TEAM
22

3-
> Professional SQL Server IDE built with WPF · **v2.1.1**
3+
> Professional SQL Server IDE built with WPF · **v2.1.2**
44
55
[![.NET](https://img.shields.io/badge/.NET-8-blueviolet)](https://dotnet.microsoft.com/)
66
[![Platform](https://img.shields.io/badge/platform-Windows-blue)](https://github.com/khalilbenaz/DBTeam/releases)
@@ -14,7 +14,7 @@
1414

1515
Connection management · Object Explorer · T-SQL editor with autocomplete + format · Schema Compare with column-level ALTER · Data Compare · Table Designer (create + edit) · Query Profiler with plan tree · Data Generator · HTML Documenter · Interactive ER Diagram · Query History & favorites · Session restore.
1616

17-
**📥 Download installer**: [DBTeam-Setup-2.1.1.exe](https://github.com/khalilbenaz/DBTeam/releases/latest) or [portable ZIP](https://github.com/khalilbenaz/DBTeam/releases/latest)
17+
**📥 Download installer**: [DBTeam-Setup-2.1.2.exe](https://github.com/khalilbenaz/DBTeam/releases/latest) or [portable ZIP](https://github.com/khalilbenaz/DBTeam/releases/latest)
1818
**🌐 Website**: <https://khalilbenaz.github.io/DBTeam/>
1919
**📚 Docs**: [Install](docs/INSTALL.md) · [User guide](docs/USER-GUIDE.md) · [Architecture](docs/ARCHITECTURE.md) · [Modules](docs/MODULES.md) · [Changelog](CHANGELOG.md)
2020

src/DBTeam.App/Lang/en-US.xaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<sys:String x:Key="Menu.About">_About DB TEAM</sys:String>
1616
<sys:String x:Key="Menu.Shortcuts">Keyboard _Shortcuts</sys:String>
1717
<sys:String x:Key="About.Title">About DB TEAM</sys:String>
18-
<sys:String x:Key="About.Version">Version 2.1.1</sys:String>
18+
<sys:String x:Key="About.Version">Version 2.1.2</sys:String>
1919
<sys:String x:Key="About.Description">Professional SQL Server IDE built with WPF.</sys:String>
2020
<sys:String x:Key="About.Close">Close</sys:String>
2121
<sys:String x:Key="About.Author">Author · Khalil Benazzouz</sys:String>
@@ -184,6 +184,12 @@
184184
<sys:String x:Key="Qe.ExportXml">Export to XML</sys:String>
185185
<sys:String x:Key="Qe.DbContext">Database context for this tab</sys:String>
186186
<sys:String x:Key="Qe.RefreshDb">Refresh database list</sys:String>
187+
<sys:String x:Key="Qe.Zoom">Zoom</sys:String>
188+
<sys:String x:Key="Qe.ZoomIn">Zoom in</sys:String>
189+
<sys:String x:Key="Qe.ZoomOut">Zoom out</sys:String>
190+
<sys:String x:Key="Qe.ZoomReset">Reset zoom</sys:String>
191+
<sys:String x:Key="Qe.Timeout">Timeout</sys:String>
192+
<sys:String x:Key="Qe.TimeoutTip">Query execution timeout in seconds (0 = unlimited)</sys:String>
187193

188194
<sys:String x:Key="Dbg.AttachTip">Parse script + open connection</sys:String>
189195
<sys:String x:Key="Dbg.ContinueTip">Continue until next breakpoint or end</sys:String>

src/DBTeam.App/Lang/fr-FR.xaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<sys:String x:Key="Menu.About">À _propos de DB TEAM</sys:String>
1616
<sys:String x:Key="Menu.Shortcuts">_Raccourcis clavier</sys:String>
1717
<sys:String x:Key="About.Title">À propos de DB TEAM</sys:String>
18-
<sys:String x:Key="About.Version">Version 2.1.1</sys:String>
18+
<sys:String x:Key="About.Version">Version 2.1.2</sys:String>
1919
<sys:String x:Key="About.Description">IDE SQL Server professionnel développé en WPF.</sys:String>
2020
<sys:String x:Key="About.Close">Fermer</sys:String>
2121
<sys:String x:Key="About.Author">Auteur · Khalil Benazzouz</sys:String>
@@ -184,6 +184,12 @@
184184
<sys:String x:Key="Qe.ExportXml">Exporter en XML</sys:String>
185185
<sys:String x:Key="Qe.DbContext">Contexte de base de données pour cet onglet</sys:String>
186186
<sys:String x:Key="Qe.RefreshDb">Rafraîchir la liste des bases</sys:String>
187+
<sys:String x:Key="Qe.Zoom">Zoom</sys:String>
188+
<sys:String x:Key="Qe.ZoomIn">Agrandir</sys:String>
189+
<sys:String x:Key="Qe.ZoomOut">Réduire</sys:String>
190+
<sys:String x:Key="Qe.ZoomReset">Réinitialiser le zoom</sys:String>
191+
<sys:String x:Key="Qe.Timeout">Timeout</sys:String>
192+
<sys:String x:Key="Qe.TimeoutTip">Délai d'expiration de la requête en secondes (0 = aucun)</sys:String>
187193

188194
<sys:String x:Key="Dbg.AttachTip">Analyser le script + ouvrir la connexion</sys:String>
189195
<sys:String x:Key="Dbg.ContinueTip">Continuer jusqu'au prochain point d'arrêt ou à la fin</sys:String>

src/DBTeam.App/Themes/AppStyles.xaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@
9090
<Setter Property="Padding" Value="14,8"/>
9191
<Setter Property="FontWeight" Value="SemiBold"/>
9292
<Setter Property="Cursor" Value="Hand"/>
93+
<Setter Property="HorizontalContentAlignment" Value="Center"/>
94+
<Setter Property="VerticalContentAlignment" Value="Center"/>
9395
<Setter Property="Template">
9496
<Setter.Value>
9597
<ControlTemplate TargetType="Button">

src/Modules/DBTeam.Modules.QueryEditor/Intellisense/CompletionItem.cs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,65 @@ public SqlCompletionItem(string text, CompletionKind kind, string? description =
3333

3434
public void Complete(TextArea textArea, ISegment completionSegment, EventArgs insertionRequestEventArgs)
3535
{
36-
textArea.Document.Replace(completionSegment, Text);
36+
textArea.Document.Replace(completionSegment, ToInsertionText());
3737
}
38+
39+
/// <summary>
40+
/// Auto-brackets object identifiers so names colliding with T-SQL reserved
41+
/// keywords (e.g. <c>transaction</c>, <c>user</c>, <c>order</c>) parse correctly.
42+
/// Tables / views / procedures / functions are always qualified;
43+
/// columns are bracketed only when the name is a reserved keyword.
44+
/// </summary>
45+
private string ToInsertionText()
46+
{
47+
switch (Kind)
48+
{
49+
case CompletionKind.Table:
50+
case CompletionKind.View:
51+
case CompletionKind.Procedure:
52+
case CompletionKind.Function:
53+
{
54+
var parts = Text.Split('.');
55+
if (parts.Length == 2) return $"[{parts[0]}].[{parts[1]}]";
56+
return IsReserved(Text) || NeedsQuoting(Text) ? $"[{Text}]" : Text;
57+
}
58+
case CompletionKind.Column:
59+
return IsReserved(Text) || NeedsQuoting(Text) ? $"[{Text}]" : Text;
60+
default:
61+
return Text;
62+
}
63+
}
64+
65+
private static bool NeedsQuoting(string s)
66+
{
67+
if (string.IsNullOrEmpty(s)) return false;
68+
foreach (var c in s) if (!(char.IsLetterOrDigit(c) || c == '_')) return true;
69+
return char.IsDigit(s[0]);
70+
}
71+
72+
private static readonly System.Collections.Generic.HashSet<string> Reserved =
73+
new(System.StringComparer.OrdinalIgnoreCase)
74+
{
75+
"ADD","ALL","ALTER","AND","ANY","AS","ASC","AUTHORIZATION","BACKUP","BEGIN","BETWEEN",
76+
"BREAK","BROWSE","BULK","BY","CASCADE","CASE","CHECK","CHECKPOINT","CLOSE","CLUSTERED",
77+
"COALESCE","COLLATE","COLUMN","COMMIT","COMPUTE","CONSTRAINT","CONTAINS","CONTAINSTABLE",
78+
"CONTINUE","CONVERT","CREATE","CROSS","CURRENT","CURRENT_DATE","CURRENT_TIME","CURRENT_TIMESTAMP",
79+
"CURRENT_USER","CURSOR","DATABASE","DBCC","DEALLOCATE","DECLARE","DEFAULT","DELETE","DENY","DESC",
80+
"DISK","DISTINCT","DISTRIBUTED","DOUBLE","DROP","DUMP","ELSE","END","ERRLVL","ESCAPE","EXCEPT",
81+
"EXEC","EXECUTE","EXISTS","EXIT","EXTERNAL","FETCH","FILE","FILLFACTOR","FOR","FOREIGN","FREETEXT",
82+
"FREETEXTTABLE","FROM","FULL","FUNCTION","GOTO","GRANT","GROUP","HAVING","HOLDLOCK","IDENTITY",
83+
"IDENTITYCOL","IDENTITY_INSERT","IF","IN","INDEX","INNER","INSERT","INTERSECT","INTO","IS","JOIN",
84+
"KEY","KILL","LEFT","LIKE","LINENO","LOAD","MERGE","NATIONAL","NOCHECK","NONCLUSTERED","NOT","NULL",
85+
"NULLIF","OF","OFF","OFFSETS","ON","OPEN","OPENDATASOURCE","OPENQUERY","OPENROWSET","OPENXML",
86+
"OPTION","OR","ORDER","OUTER","OVER","PERCENT","PIVOT","PLAN","PRECISION","PRIMARY","PRINT","PROC",
87+
"PROCEDURE","PUBLIC","RAISERROR","READ","READTEXT","RECONFIGURE","REFERENCES","REPLICATION","RESTORE",
88+
"RESTRICT","RETURN","REVERT","REVOKE","RIGHT","ROLLBACK","ROWCOUNT","ROWGUIDCOL","RULE","SAVE","SCHEMA",
89+
"SECURITYAUDIT","SELECT","SEMANTICKEYPHRASETABLE","SEMANTICSIMILARITYDETAILSTABLE","SEMANTICSIMILARITYTABLE",
90+
"SESSION_USER","SET","SETUSER","SHUTDOWN","SOME","STATISTICS","SYSTEM_USER","TABLE","TABLESAMPLE","TEXTSIZE",
91+
"THEN","TO","TOP","TRAN","TRANSACTION","TRIGGER","TRUNCATE","TRY_CONVERT","TSEQUAL","UNION","UNIQUE","UNPIVOT",
92+
"UPDATE","UPDATETEXT","USE","USER","VALUES","VARYING","VIEW","WAITFOR","WHEN","WHERE","WHILE","WITH","WITHIN GROUP",
93+
"WRITETEXT"
94+
};
95+
96+
private static bool IsReserved(string s) => Reserved.Contains(s);
3897
}

src/Modules/DBTeam.Modules.QueryEditor/ViewModels/QueryEditorViewModel.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ public QueryEditorViewModel(IQueryExecutionService exec, IDatabaseMetadataServic
3636
[ObservableProperty] private string messages = string.Empty;
3737
[ObservableProperty] private TimeSpan elapsed;
3838
[ObservableProperty] private int rowCount;
39+
[ObservableProperty] private int commandTimeoutSeconds = 120;
40+
[ObservableProperty] private double zoomFactor = 1.0;
41+
42+
[RelayCommand] private void ZoomIn() => ZoomFactor = System.Math.Min(2.5, System.Math.Round(ZoomFactor + 0.1, 2));
43+
[RelayCommand] private void ZoomOut() => ZoomFactor = System.Math.Max(0.5, System.Math.Round(ZoomFactor - 0.1, 2));
44+
[RelayCommand] private void ZoomReset() => ZoomFactor = 1.0;
3945

4046
public ObservableCollection<string> Databases { get; }
4147
public ObservableCollection<DataTable> Results { get; } = new();
@@ -69,7 +75,7 @@ public async Task ExecuteAsync()
6975
Results.Clear();
7076
Messages = string.Empty;
7177
StatusText = "Executing...";
72-
var req = new QueryRequest { Sql = Sql, Database = Database };
78+
var req = new QueryRequest { Sql = Sql, Database = Database, CommandTimeoutSeconds = CommandTimeoutSeconds };
7379
var r = await _exec.ExecuteAsync(Connection, req);
7480
foreach (var ds in r.ResultSets) Results.Add(ds);
7581
Messages = string.Join(Environment.NewLine, r.Messages);

src/Modules/DBTeam.Modules.QueryEditor/Views/QueryEditorView.xaml

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,43 @@
8585
<TextBlock Text="{DynamicResource Label.Results}"/>
8686
</StackPanel>
8787
</TabItem.Header>
88-
<ItemsControl ItemsSource="{Binding Results}">
89-
<ItemsControl.ItemTemplate>
90-
<DataTemplate>
91-
<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="True" IsReadOnly="True" Margin="0,0,0,8"/>
92-
</DataTemplate>
93-
</ItemsControl.ItemTemplate>
94-
</ItemsControl>
88+
<DockPanel>
89+
<Border DockPanel.Dock="Top" Padding="6,4" Background="{DynamicResource SystemControlBackgroundChromeMediumLowBrush}">
90+
<StackPanel Orientation="Horizontal">
91+
<TextBlock Text="{DynamicResource Qe.Zoom}" VerticalAlignment="Center" Margin="0,0,6,0" Opacity="0.7"/>
92+
<Button Command="{Binding ZoomOutCommand}" Style="{StaticResource IconButton}" ToolTip="{DynamicResource Qe.ZoomOut}" Padding="4">
93+
<md:PackIcon Kind="MagnifyMinus" Width="14" Height="14"/>
94+
</Button>
95+
<TextBlock Text="{Binding ZoomFactor, StringFormat={}{0:P0}}" VerticalAlignment="Center" Margin="4,0" MinWidth="40" TextAlignment="Center"/>
96+
<Button Command="{Binding ZoomInCommand}" Style="{StaticResource IconButton}" ToolTip="{DynamicResource Qe.ZoomIn}" Padding="4">
97+
<md:PackIcon Kind="MagnifyPlus" Width="14" Height="14"/>
98+
</Button>
99+
<Button Command="{Binding ZoomResetCommand}" Style="{StaticResource IconButton}" ToolTip="{DynamicResource Qe.ZoomReset}" Padding="4" Margin="0,0,12,0">
100+
<md:PackIcon Kind="Restore" Width="14" Height="14"/>
101+
</Button>
102+
<TextBlock Text="{DynamicResource Qe.Timeout}" VerticalAlignment="Center" Opacity="0.7" Margin="0,0,6,0"/>
103+
<TextBox Text="{Binding CommandTimeoutSeconds, UpdateSourceTrigger=PropertyChanged}" Width="60" VerticalAlignment="Center" ToolTip="{DynamicResource Qe.TimeoutTip}"/>
104+
<TextBlock Text="s" VerticalAlignment="Center" Margin="2,0,0,0" Opacity="0.6"/>
105+
</StackPanel>
106+
</Border>
107+
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
108+
<ItemsControl ItemsSource="{Binding Results}">
109+
<ItemsControl.ItemTemplate>
110+
<DataTemplate>
111+
<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="True" IsReadOnly="True" Margin="0,0,0,8"
112+
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
113+
MaxHeight="500" ColumnWidth="SizeToHeader"
114+
EnableRowVirtualization="True" EnableColumnVirtualization="True">
115+
<DataGrid.LayoutTransform>
116+
<ScaleTransform ScaleX="{Binding DataContext.ZoomFactor, RelativeSource={RelativeSource AncestorType=UserControl}}"
117+
ScaleY="{Binding DataContext.ZoomFactor, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
118+
</DataGrid.LayoutTransform>
119+
</DataGrid>
120+
</DataTemplate>
121+
</ItemsControl.ItemTemplate>
122+
</ItemsControl>
123+
</ScrollViewer>
124+
</DockPanel>
95125
</TabItem>
96126
<TabItem>
97127
<TabItem.Header>

0 commit comments

Comments
 (0)