7

基于WPF重复造轮子,写一款数据库文档管理工具(一) - 月亮邮递员

 2 years ago
source link: https://www.cnblogs.com/izhaofu/p/smartsql1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

基于WPF重复造轮子,写一款数据库文档管理工具(一)

公司业务历史悠久且复杂,数据库的表更是多而繁杂,每次基于老业务做功能开发都需要去翻以前的表和业务代码。需要理解旧的表的用途以及包含的字段的含义,表少还好说,但是表一多这就很浪费时间,而且留下来的文档都是残缺不全,每次查一些表的含义都要捯饬很久。在网上搜索关于数据库文档管理工具搜到最多的就是Screw和DBCHM,一个是基于Java的工具、另一个则是bug很多,表一多就一直转圈圈进不去。所以自己就动手开发了这款SmartSQL的工具。

627089-20220731162025265-1116344830.png

它是一款基于.Net 4.6.1WPF开发的一款数据库文档管理,不仅支持多种数据库(SQLServerMySQLPostgreSQLSQLite)表、视图、存储过程的查询管理,还支持对其进行导出成离线文档,支持的文档包括CHMWordExcelPDFHTMLXmlJsonMarkDown等多种格式。

现在将它开源分享出来,供更多的小伙伴使用和参考学习(文末附开源地址)。

  • .Net 4.6.1
  • WPF
  • HandyControl
  • SqlSugar
  • AvalonEdit
  • SharpVectors

HandyControl是一款非常优秀的WPF框架,做出来的页面都很漂亮,所以我们选择使用它。
Nuget中引用HandyControl

627089-20220731160825057-268841996.png

一.菜单栏

627089-20220731162248818-1700510450.png

然后我们要实现一个基于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控件、也有ContextMenuhc:LoadingLine等控件,还有自己写的自定义控件。

其实WPF要比WinForm好用不少,不仅支持MVVM数据绑定还支持灵活的页面渲染,自从用了WPF再也不用WinForm了。

今天分享暂时到这里,下一篇讲介绍DataGrid表格数据绑定及相关条件搜索。下面是工具的开源地址,感兴趣的可以Clone下来学习一下。码砖不易,喜欢的麻烦点下Star.

https://gitee.com/izhaofu/SmartSQL

627089-20220731163845669-2112065971.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK