项目背景
公司业务历史悠久且复杂,数据库的表更是多而繁杂,每次基于老业务做功能开发都需要去翻以前的表和业务代码。需要理解旧的表的用途以及包含的字段的含义,表少还好说,但是表一多这就很浪费时间,而且留下来的文档都是残缺不全,每次查一些表的含义都要捯饬很久。在网上搜索关于数据库文档管理工具搜到最多的就是Screw和DBCHM,一个是基于Java的工具、另一个则是bug很多,表一多就一直转圈圈进不去。所以自己就动手开发了这款SmartSQL的工具。
它是一款基于.Net 4.6.1、WPF开发的一款数据库文档管理,不仅支持多种数据库(SQLServer、MySQL、PostgreSQL、SQLite)表、视图、存储过程的查询管理,还支持对其进行导出成离线文档,支持的文档包括CHM、Word、Excel、PDF、HTML、Xml、Json、MarkDown等多种格式。
现在将它开源分享出来,供更多的小伙伴使用和参考学习(文末附开源地址)。
技术栈
.Net 4.6.1
WPF
HandyControl
SqlSugar
AvalonEdit
SharpVectors
HandyControl是一款非常优秀的WPF框架,做出来的页面都很漂亮,所以我们选择使用它。
Nuget中引用HandyControl:
一.菜单栏
然后我们要实现一个基于WPF边框上的菜单栏,刚好HandyControl
中有这么一个菜单栏的控件,
下面就是实现菜单栏的方法:
<hc:GlowWindow.NonClientAreaContent><StackPanel Height="29" Margin="25,0,0,0"><Menu HorizontalAlignment="Left"><MenuItemx:Name="SwitchMenu"Cursor="Hand"FontWeight="Bold"Foreground="{DynamicResource DarkPrimaryBrush}"Header="选择连接"><MenuItem.Icon><PathData="{StaticResource DownGeometry}"Fill="{DynamicResource DarkPrimaryBrush}"Stretch="Uniform" /></MenuItem.Icon><MenuItem.ItemTemplate><HierarchicalDataTemplate><MenuItemWidth="160"Margin="0"Padding="0"HorizontalAlignment="Left"VerticalAlignment="Stretch"Click="SwitchMenu_Click"Cursor="Hand"FontWeight="Normal"Header="{Binding ConnectName}"><MenuItem.Icon><svgc:SvgViewboxWidth="16"Height="16"HorizontalAlignment="Left"IsHitTestVisible="False"Source="{Binding Icon}" /></MenuItem.Icon></MenuItem></HierarchicalDataTemplate></MenuItem.ItemTemplate></MenuItem><MenuItemName="MenuConnect"Cursor="Hand"FontWeight="Bold"Foreground="{DynamicResource DarkPrimaryBrush}"Header="文件"><MenuItem.Icon><PathData="{StaticResource FileGeometry}"Fill="{DynamicResource DarkPrimaryBrush}"Stretch="Uniform" /></MenuItem.Icon><MenuItemName="AddConnect"Click="AddConnect_OnClick"FontWeight="Normal"Header="新建连接"><MenuItem.Icon><PathData="{StaticResource NewConnectGeometry}"Fill="{DynamicResource DarkPrimaryBrush}"Stretch="Uniform" /></MenuItem.Icon></MenuItem><MenuItemName="ImportMark"Click="ImportMark_OnClick"FontWeight="Normal"Header="导入备注"><MenuItem.Icon><PathData="{StaticResource ImportGeometry}"Fill="{DynamicResource DarkPrimaryBrush}"Stretch="Uniform" /></MenuItem.Icon></MenuItem><MenuItemName="ExportDoc"Click="ExportDoc_OnClick"FontWeight="Normal"Header="导出文档"><MenuItem.Icon><PathData="{StaticResource ExportGeometry}"Fill="{DynamicResource DarkPrimaryBrush}"Stretch="Uniform" /></MenuItem.Icon></MenuItem></MenuItem><MenuItemName="MenuGroup"Click="MenuGroup_OnClick"Cursor="Hand"FontWeight="Bold"Foreground="{DynamicResource DarkPrimaryBrush}"Header="分组"><MenuItem.Icon><PathData="{StaticResource GroupGeometry}"Fill="{DynamicResource DarkPrimaryBrush}"Stretch="Uniform" /></MenuItem.Icon></MenuItem><MenuItemName="MenuSetting"Click="MenuSetting_OnClick"Cursor="Hand"FontWeight="Bold"Foreground="{DynamicResource DarkPrimaryBrush}"Header="设置"><MenuItem.Icon><PathData="{StaticResource SettingGeometry}"Fill="{DynamicResource DarkPrimaryBrush}"Stretch="Uniform" /></MenuItem.Icon></MenuItem><MenuItemName="MenuAbout"Click="MenuAbout_OnClick"Cursor="Hand"FontWeight="Bold"Foreground="{DynamicResource DarkPrimaryBrush}"Header="关于"><MenuItem.Icon><PathData="{StaticResource InfoGeometry}"Fill="{DynamicResource DarkPrimaryBrush}"Stretch="Uniform" /></MenuItem.Icon></MenuItem></Menu></StackPanel>
</hc:GlowWindow.NonClientAreaContent>
<!-- 工具栏菜单 -->
其中有个小插曲,在WPF中是默认不支持svg图形的,所以我们需要引用一个组件:SharpVectors
,它的使用方法是这样的,引用svg界面需要引入下面语句:xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
然后引用要显示的svg图形:
<svgc:SvgViewboxWidth="16"Height="16"HorizontalAlignment="Left"IsHitTestVisible="False"Source="{Binding Icon}" />
二.左侧菜单栏
然后就是左侧的菜单栏,我们要实现一个数据库的选择和数据库对象的搜索,可以搜索相关表、视图、存储过程等对象。
首先我们要对我们的主界面进行一个简单的1:1:1的竖向布局,分别为左侧菜单栏、中间可以移动的分隔栏、右面的主界面:
<!-- Main区域 -->
<Grid x:Name="GridMain" Background="{StaticResource CloudDrawingBrush}"><Grid.RowDefinitions><RowDefinition Height="*" /></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="3.3*" MinWidth="200" /><ColumnDefinition Width="Auto" /><ColumnDefinition Width="6.6*" /></Grid.ColumnDefinitions>
</Grid>
现在我们要实现一个左侧树形的菜单栏,我们使用的是WPF里面的TreeView
控件进行实现这样一个功能,下面是相关代码:
<DockPanel Grid.Row="0" Grid.Column="0"><hc:SimplePanel><BorderMargin="5,5,0,5"Background="{DynamicResource RegionBrush}"CornerRadius="{Binding CornerRadius}"><GridHeight="Auto"Margin="5"Background="Transparent"><TextBox x:Name="HidSelectDatabase" Visibility="Hidden" /><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="8*" /><ColumnDefinition Width="1*" MinWidth="30" /></Grid.ColumnDefinitions><ComboBoxx:Name="SelectDatabase"Height="30"VerticalAlignment="Top"HorizontalContentAlignment="Stretch"hc:BorderElement.CornerRadius="5"hc:InfoElement.Placeholder="请选择数据库"Cursor="Hand"IsTextSearchEnabled="True"SelectionChanged="SelectDatabase_OnSelectionChanged"Style="{StaticResource ComboBoxExtend}"Text="{Binding DbName}"><ComboBox.ItemTemplate><DataTemplate><StackPanel VerticalAlignment="Center" Orientation="Horizontal"><ImageWidth="11"Height="15"Source="/SmartSQL;component/Resources/Img/dataBase.ico" /><TextBlockMargin="5,0,0,0"HorizontalAlignment="Center"VerticalAlignment="Center"Text="{Binding DbName}" /></StackPanel></DataTemplate></ComboBox.ItemTemplate></ComboBox><ButtonName="BtnFresh"Grid.Column="2"Margin="0,0,0,0"Padding="4"VerticalAlignment="Top"Background="Transparent"BorderThickness="0"Click="BtnFresh_OnClick"Cursor="Hand"><Button.Content><Image Source="/SmartSQL;component/Resources/Img/Refresh.png" Stretch="Fill" /></Button.Content></Button></Grid><hc:SearchBarx:Name="SearchMenu"Height="30"Margin="0,34,0,0"Padding="5,0,5,0"VerticalAlignment="Top"HorizontalContentAlignment="Stretch"hc:BorderElement.CornerRadius="5"hc:InfoElement.Placeholder="搜索数据表/视图/存储过程"FontSize="13"ShowClearButton="True"Style="{StaticResource SearchBarPlus}"TextChanged="SearchMenu_OnTextChanged" /><TabControlx:Name="TabLeftType"Margin="0,65,0,40"SelectionChanged="TabLeftType_OnSelectionChanged"Style="{StaticResource TabControlInLine}"><TabItemx:Name="TabAllData"Cursor="Hand"Header="全部"IsSelected="True" /><TabItemx:Name="TabGroupData"Cursor="Hand"Header="分组"IsSelected="False" /><!--<TabItemx:Name="TabFavData"Cursor="Hand"Header="收藏"IsSelected="False" />--></TabControl><TreeViewx:Name="TreeViewTables"Margin="0,100,0,0"VerticalAlignment="Top"BorderThickness="0"ItemsSource="{Binding TreeViewData}"SelectedItemChanged="SelectedTable_OnClick"><TreeView.ItemContainerStyle><Style BasedOn="{StaticResource TreeViewItemBaseStyle}" TargetType="{x:Type TreeViewItem}"><Setter Property="IsExpanded" Value="{Binding IsExpanded}" /><Setter Property="FontWeight" Value="{Binding FontWeight}" /><Setter Property="FontSize" Value="12" /><Setter Property="Visibility" Value="{Binding Visibility}" /><Setter Property="Foreground" Value="{Binding TextColor}" /><Setter Property="Cursor" Value="Hand" /><!-- 禁止水平滚动条自动滚动 --><EventSetter Event="RequestBringIntoView" Handler="EventSetter_OnHandler" /><Style.Triggers><Trigger Property="IsSelected" Value="True"><Setter Property="FontWeight" Value="Bold" /></Trigger></Style.Triggers>
</Style></TreeView.ItemContainerStyle><TreeView.ContextMenu><!-- 右键菜单 --><ContextMenu Visibility="Visible"><MenuItemx:Name="MenuSelectedItem"Padding="5,0,5,0"VerticalAlignment="Center"Click="MenuSelectedItem_OnClick"Cursor="Hand"Header="复制对象名" /></ContextMenu></TreeView.ContextMenu><TreeView.ItemTemplate><HierarchicalDataTemplate DataType="{x:Type models:TreeNodeItem}" ItemsSource="{Binding Children}"><StackPanel Orientation="Horizontal"><svgc:SvgViewboxWidth="12"Height="12"Margin="0,0,5,0"HorizontalAlignment="Left"Source="{Binding Icon}" /><TextBlockVerticalAlignment="Center"FontSize="12"Text="{Binding DisplayName}"ToolTip="{Binding DisplayName}" /></StackPanel></HierarchicalDataTemplate></TreeView.ItemTemplate></TreeView><Gridx:Name="NoDataText"Margin="0,100,0,5"HorizontalAlignment="Stretch"Background="White"Cursor="Arrow"><local:NoDataAreax:Name="NoDataAreaText"Margin="0"HorizontalAlignment="Center"ShowType="All" /></Grid><GridMargin="0"VerticalAlignment="Bottom"Visibility="Hidden"><Grid.ColumnDefinitions><ColumnDefinition Width="4*" /><ColumnDefinition Width="6*" /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions><Grid><ComboBoxx:Name="CbTargetConnect"Height="26"VerticalAlignment="Bottom"HorizontalContentAlignment="Left"hc:InfoElement.Placeholder="目标连接"Cursor="Hand"DisplayMemberPath="ConnectName"IsTextSearchEnabled="True"SelectedValuePath="DbMasterConnectString"SelectionChanged="CbTargetConnect_OnSelectionChanged"Style="{StaticResource ComboBoxExtend}" /></Grid><Grid Grid.Column="1" Margin="5,0,0,0"><ComboBoxx:Name="CbTargetDatabase"MinWidth="50"VerticalAlignment="Bottom"HorizontalContentAlignment="Left"hc:InfoElement.Placeholder="目标数据库"Cursor="Hand"IsTextSearchEnabled="True"Style="{StaticResource ComboBoxExtend}" /></Grid><Grid Grid.Column="2"><!-- 差异比较按钮 --><Buttonx:Name="BtnCompare"Height="30"Margin="5,5,0,0"HorizontalAlignment="Right"hc:BorderElement.CornerRadius="6"hc:IconElement.Geometry="{StaticResource CompareGeometry}"Click="BtnCompare_OnClick"Content="差异比较"Cursor="Hand" /></Grid></Grid><!-- 数据加载Loading --><hc:LoadingLinex:Name="LoadingLine"Margin="0,0,0,0"Visibility="Collapsed" /></Grid></Border></hc:SimplePanel></DockPanel>
在这里我没有详细介绍底层c#的相关代码,里面逻辑有些复杂感兴趣的可以去我的开源项目中学习。在上面的左侧菜单代码中,我们使用的不仅有TreeView
控件、也有ContextMenu
、hc:LoadingLine
等控件,还有自己写的自定义控件。
其实WPF要比WinForm好用不少,不仅支持MVVM数据绑定还支持灵活的页面渲染,自从用了WPF再也不用WinForm了。
今天分享暂时到这里,下一篇将介绍DataGrid表格数据绑定及相关条件搜索。下面是工具的开源地址,感兴趣的可以Clone下来学习一下。码砖不易,喜欢的麻烦点下Star.
开源地址
https://gitee.com/izhaofu/SmartSQL