- A+
项目背景
公司业务历史悠久且复杂,数据库的表更是多而繁杂,每次基于老业务做功能开发都需要去翻以前的表和业务代码。需要理解旧的表的用途以及包含的字段的含义,表少还好说,但是表一多这就很浪费时间,而且留下来的文档都是残缺不全,每次查一些表的含义都要捯饬很久。在网上搜索关于数据库文档管理工具搜到最多的就是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"> <MenuItem x:Name="SwitchMenu" Cursor="Hand" FontWeight="Bold" Foreground="{DynamicResource DarkPrimaryBrush}" Header="选择连接"> <MenuItem.Icon> <Path Data="{StaticResource DownGeometry}" Fill="{DynamicResource DarkPrimaryBrush}" Stretch="Uniform" /> </MenuItem.Icon> <MenuItem.ItemTemplate> <HierarchicalDataTemplate> <MenuItem Width="160" Margin="0" Padding="0" HorizontalAlignment="Left" VerticalAlignment="Stretch" Click="SwitchMenu_Click" Cursor="Hand" FontWeight="Normal" Header="{Binding ConnectName}"> <MenuItem.Icon> <svgc:SvgViewbox Width="16" Height="16" HorizontalAlignment="Left" IsHitTestVisible="False" Source="{Binding Icon}" /> </MenuItem.Icon> </MenuItem> </HierarchicalDataTemplate> </MenuItem.ItemTemplate> </MenuItem> <MenuItem Name="MenuConnect" Cursor="Hand" FontWeight="Bold" Foreground="{DynamicResource DarkPrimaryBrush}" Header="文件"> <MenuItem.Icon> <Path Data="{StaticResource FileGeometry}" Fill="{DynamicResource DarkPrimaryBrush}" Stretch="Uniform" /> </MenuItem.Icon> <MenuItem Name="AddConnect" Click="AddConnect_OnClick" FontWeight="Normal" Header="新建连接"> <MenuItem.Icon> <Path Data="{StaticResource NewConnectGeometry}" Fill="{DynamicResource DarkPrimaryBrush}" Stretch="Uniform" /> </MenuItem.Icon> </MenuItem> <MenuItem Name="ImportMark" Click="ImportMark_OnClick" FontWeight="Normal" Header="导入备注"> <MenuItem.Icon> <Path Data="{StaticResource ImportGeometry}" Fill="{DynamicResource DarkPrimaryBrush}" Stretch="Uniform" /> </MenuItem.Icon> </MenuItem> <MenuItem Name="ExportDoc" Click="ExportDoc_OnClick" FontWeight="Normal" Header="导出文档"> <MenuItem.Icon> <Path Data="{StaticResource ExportGeometry}" Fill="{DynamicResource DarkPrimaryBrush}" Stretch="Uniform" /> </MenuItem.Icon> </MenuItem> </MenuItem> <MenuItem Name="MenuGroup" Click="MenuGroup_OnClick" Cursor="Hand" FontWeight="Bold" Foreground="{DynamicResource DarkPrimaryBrush}" Header="分组"> <MenuItem.Icon> <Path Data="{StaticResource GroupGeometry}" Fill="{DynamicResource DarkPrimaryBrush}" Stretch="Uniform" /> </MenuItem.Icon> </MenuItem> <MenuItem Name="MenuSetting" Click="MenuSetting_OnClick" Cursor="Hand" FontWeight="Bold" Foreground="{DynamicResource DarkPrimaryBrush}" Header="设置"> <MenuItem.Icon> <Path Data="{StaticResource SettingGeometry}" Fill="{DynamicResource DarkPrimaryBrush}" Stretch="Uniform" /> </MenuItem.Icon> </MenuItem> <MenuItem Name="MenuAbout" Click="MenuAbout_OnClick" Cursor="Hand" FontWeight="Bold" Foreground="{DynamicResource DarkPrimaryBrush}" Header="关于"> <MenuItem.Icon> <Path Data="{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:SvgViewbox Width="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> <Border Margin="5,5,0,5" Background="{DynamicResource RegionBrush}" CornerRadius="{Binding CornerRadius}"> <Grid Height="Auto" Margin="5" Background="Transparent"> <TextBox x:Name="HidSelectDatabase" Visibility="Hidden" /> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="8*" /> <ColumnDefinition Width="1*" MinWidth="30" /> </Grid.ColumnDefinitions> <ComboBox x: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"> <Image Width="11" Height="15" Source="/SmartSQL;component/Resources/Img/dataBase.ico" /> <TextBlock Margin="5,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding DbName}" /> </StackPanel> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> <Button Name="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:SearchBar x: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" /> <TabControl x:Name="TabLeftType" Margin="0,65,0,40" SelectionChanged="TabLeftType_OnSelectionChanged" Style="{StaticResource TabControlInLine}"> <TabItem x:Name="TabAllData" Cursor="Hand" Header="全部" IsSelected="True" /> <TabItem x:Name="TabGroupData" Cursor="Hand" Header="分组" IsSelected="False" /> <!--<TabItem x:Name="TabFavData" Cursor="Hand" Header="收藏" IsSelected="False" />--> </TabControl> <TreeView x: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"> <MenuItem x: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:SvgViewbox Width="12" Height="12" Margin="0,0,5,0" HorizontalAlignment="Left" Source="{Binding Icon}" /> <TextBlock VerticalAlignment="Center" FontSize="12" Text="{Binding DisplayName}" ToolTip="{Binding DisplayName}" /> </StackPanel> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> <Grid x:Name="NoDataText" Margin="0,100,0,5" HorizontalAlignment="Stretch" Background="White" Cursor="Arrow"> <local:NoDataArea x:Name="NoDataAreaText" Margin="0" HorizontalAlignment="Center" ShowType="All" /> </Grid> <Grid Margin="0" VerticalAlignment="Bottom" Visibility="Hidden"> <Grid.ColumnDefinitions> <ColumnDefinition Width="4*" /> <ColumnDefinition Width="6*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid> <ComboBox x: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"> <ComboBox x:Name="CbTargetDatabase" MinWidth="50" VerticalAlignment="Bottom" HorizontalContentAlignment="Left" hc:InfoElement.Placeholder="目标数据库" Cursor="Hand" IsTextSearchEnabled="True" Style="{StaticResource ComboBoxExtend}" /> </Grid> <Grid Grid.Column="2"> <!-- 差异比较按钮 --> <Button x: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:LoadingLine x: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