Click here to Skip to main content
15,896,730 members
Articles / Programming Languages / SQL

A Visual SQL Query Designer

Rate me:
Please Sign up or sign in to vote.
4.94/5 (74 votes)
23 Oct 2009CPOL15 min read 392.6K   23K   292  
This article describes the implementation of a QueryDesignerDialog class that allows users to create SQL queries based on a given OLEDB connection string.
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Data.OleDb;
using Microsoft.Win32;

namespace QueryDesigner
{
    public partial class Form1 : Form
    {
        //------------------------------------------------------------------------------
        #region ** fields

        // current connection string and corresponding schema
        string _connString;
        OleDbSchema _schema;

        // max number of records shown on the preview dialog
        const int MAX_PREVIEW_RECORDS = 5000;

        #endregion

        //------------------------------------------------------------------------------
        #region ** ctor

        public Form1()
        {
            InitializeComponent();

            // make combo owner-drawn
            var cmb = _cmbConnString.ComboBox;
            cmb.DrawMode = DrawMode.OwnerDrawFixed;
            cmb.DrawItem += cmb_DrawItem;

            // load recently used connection strings
            var mru = Properties.Settings.Default.RecentConnections;
            if (mru != null)
            {
                foreach (string connString in mru)
                {
                    _cmbConnString.Items.Add(connString);
                }
            }

            // autosize toolstrip
            _toolStrip.Layout += _toolStrip_Layout;
            _tab.SelectedIndexChanged += _tab_SelectedIndexChanged;
        }

        #endregion

        //------------------------------------------------------------------------------
        #region ** object model

        /// <summary>
        /// Gets or sets the connection string.
        /// </summary>
        public string ConnectionString
        {
            get { return _connString; }
            set
            {
                if (value != ConnectionString)
                {
                    // this may take a while
                    Cursor = Cursors.WaitCursor;

                    // look for item in the list
                    var items = _cmbConnString.Items;
                    var index = items.IndexOf(value);

                    // get schema for the new connection string
                    _schema = OleDbSchema.GetSchema(value);

                    // handle good connection strings
                    if (_schema != null)
                    {
                        // add good values to the list
                        if (index < 0)
                        {
                            items.Insert(0, value);
                        }
                        else if (index > 0)
                        {
                            items.RemoveAt(index);
                            items.Insert(0, value);
                        }

                        // trim list
                        while (items.Count > _cmbConnString.MaxDropDownItems)
                        {
                            items.RemoveAt(items.Count - 1);
                        }
                    }
                    else // handle bad connection strings
                    {
                        // remove from list
                        if (index >= 0)
                        {
                            items.RemoveAt(index);
                        }

                        // do not store bad values
                        value = string.Empty;
                    }

                    // save new value
                    _connString = value;

                    // show new value in combo box and table tree
                    _cmbConnString.Text = value;
                    UpdateTableTree();

                    // new connection, clear SQL
                    _txtSql.Text = string.Empty;

                    // update ui
                    UpdateUI();

                    // done
                    Cursor = null;
                }
            }
        }
        /// <summary>
        /// Gets a SQL statement that corresponds to the element that
        /// is currently selected (table, view, stored procedure, or
        /// explicit sql statement).
        /// </summary>
        public string SelectStatement
        {
            get
            {
                // table/view/sproc
                if (_tab.SelectedTab == _pgTables)
                {
                    var nd = _treeTables.SelectedNode;
                    return nd == null || nd.Tag == null || _schema == null
                        ? string.Empty
                        : OleDbSchema.GetSelectStatement(nd.Tag as DataTable);
                }
                else // explicit sql statement
                {
                    return _txtSql.Text;
                }
            }
        }

        #endregion

        //------------------------------------------------------------------------------
        #region ** event handlers

        // adjust combobox size to fill toolstrip
        void _toolStrip_Layout(object sender, LayoutEventArgs e)
        {
            int width = _toolStrip.ClientRectangle.Width - 15;
            foreach (ToolStripItem item in _toolStrip.Items)
            {
                if (item != _cmbConnString && item.Visible)
                {
                    width -= item.Width - item.Margin.Left - item.Margin.Right;
                }
            }
            width = Math.Max(100, Math.Min(500, width));
            _cmbConnString.Width = width;
        }

        // form closing: save recently used connection strings
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            var mru = new System.Collections.Specialized.StringCollection();
            foreach (string item in _cmbConnString.Items)
            {
                mru.Add(item);
            }
            Properties.Settings.Default.RecentConnections = mru;
            Properties.Settings.Default.Save();
            base.OnFormClosing(e);
        }

        // validating combo: apply new connection string
        void _cmbConnString_Validating(object sender, CancelEventArgs e)
        {
            ConnectionString = _cmbConnString.Text;
        }

        // new connection picked: apply new connection string
        void _cmbConnString_SelectedIndexChanged(object sender, EventArgs e)
        {
            ConnectionString = _cmbConnString.Text;
        }

        // user pressed enter: apply new connection string
        void _cmbConnString_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 13)
            {
                e.Handled = true;
                ConnectionString = _cmbConnString.Text;
            }
        }

        // pick a new connection
        void _btnConnPicker_Click(object sender, EventArgs e)
        {
            // release mouse capture to avoid wait cursor
            _toolStrip.Capture = false;

            // get starting connection string
            // (if empty or no provider, start with SQL source as default)
            string connString = _cmbConnString.Text;
            if (string.IsNullOrEmpty(connString) || connString.IndexOf("provider=", StringComparison.OrdinalIgnoreCase) < 0)
            {
                connString = "Provider=SQLOLEDB.1;";
            }

            // let user change it
            ConnectionString = OleDbConnString.EditConnectionString(this, connString);
        }

        // trim items in combo using ellipsis (they're very long)
        void cmb_DrawItem(object sender, DrawItemEventArgs e)
        {
            var fmt = new StringFormat();
            fmt.LineAlignment = StringAlignment.Center;
            fmt.Trimming = StringTrimming.EllipsisPath;

            var text = (string)_cmbConnString.Items[e.Index];
            text = OleDbConnString.TrimConnectionString(text);

            var brush = (e.State & DrawItemState.Selected) != 0
                ? SystemBrushes.HighlightText
                : SystemBrushes.WindowText;

            e.DrawBackground();
            e.Graphics.DrawString(text, _cmbConnString.Font, brush, e.Bounds, fmt);
            e.DrawFocusRectangle();
        }

        // preview data when user hits enter on the tree
        void _treeTables_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 13)
            {
                e.Handled = true; // << suppress beep
                PreviewData();
            }
        }

        // preview data when user double-clicks a node
        void _treeTables_DoubleClick(object sender, EventArgs e)
        {
            var nd = _treeTables.SelectedNode;
            if (nd != null && nd.Tag is DataTable)
            {
                PreviewData();
            }
        }

        // preview data when user clicks the preview button
        void _btnPreviewData_Click(object sender, EventArgs e)
        {
            PreviewData();
        }

        // enable/disable buttons when a node or tab is selected
        void _treeTables_AfterSelect(object sender, TreeViewEventArgs e)
        {
            UpdateUI();
        }
        void _tab_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateUI();
        }

        // invoke SQL builder
        void _btnSqlBuilder_Click(object sender, EventArgs e)
        {
            using (var dlg = new QueryDesignerDialog())
            {
                dlg.Font = this.Font;
                dlg.ConnectionString = ConnectionString;
                // dlg.SelectStatement = SelectStatement; // NOP (for now)
                if (dlg.ShowDialog(this) == DialogResult.OK)
                {
                    _txtSql.Text = dlg.SelectStatement;
                    _tab.SelectedTab = _pgSql;
                    UpdateUI();
                }
            }
        }

        // close the form
        void _btnOK_Click(object sender, EventArgs e)
        {
            DialogResult = DialogResult.OK;
            Close();
        }
        void _btnCancel_Click(object sender, EventArgs e)
        {
            DialogResult = DialogResult.Cancel;
            Close();
        }

        #endregion

        //------------------------------------------------------------------------------
        #region ** implementation

        // issue a warning
        void Warning(string format, params object[] args)
        {
            string msg = string.Format(format, args);
            MessageBox.Show(this, msg, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }

        // update table tree to reflect new connection string
        void UpdateTableTree()
        {
            // initialize table tree
            TreeNodeCollection nodes = _treeTables.Nodes;
            nodes.Clear();
            var ndTables = new TreeNode(Properties.Resources.Tables, 0, 0);
            var ndViews = new TreeNode(Properties.Resources.Views, 1, 1);
            var ndProcs = new TreeNode(Properties.Resources.StoredProcedures, 2, 2);

            // populate using current schema
            if (_schema != null)
            {
                // populate the tree
                _treeTables.BeginUpdate();
                foreach (DataTable dt in _schema.Tables)
                {
                    // create new node, save table in tag property
                    var node = new TreeNode(dt.TableName);
                    node.Tag = dt;

                    // add new node to appropriate parent
                    switch (OleDbSchema.GetTableType(dt))
                    {
                        case TableType.Table:
                            ndTables.Nodes.Add(node);
                            node.ImageIndex = node.SelectedImageIndex = 0;
                            break;
                        case TableType.View:
                            ndViews.Nodes.Add(node);
                            node.ImageIndex = node.SelectedImageIndex = 1;
                            break;
                        case TableType.Procedure:
                            ndProcs.Nodes.Add(node);
                            node.ImageIndex = node.SelectedImageIndex = 2;
                            break;
                    }
                }

                // add non-empty nodes to tree
                foreach (TreeNode nd in new TreeNode[] { ndTables, ndViews, ndProcs })
                {
                    if (nd.Nodes.Count > 0)
                    {
                        nd.Text = string.Format("{0} ({1})", nd.Text, nd.Nodes.Count);
                        nodes.Add(nd);
                    }
                }

                // expand tables node
                ndTables.Expand();

                // done
                _treeTables.EndUpdate();
                _tab.SelectedIndex = 0;
            }
        }

        // preview data for currently selected node
        void PreviewData()
        {
            // make sure we have a select statement
            var sql = SelectStatement;
            if (string.IsNullOrEmpty(sql))
            {
                return;
            }

            // create table to load with data and display
            var dt = new DataTable("Query");

            // if a table/view is selected, get table name and parameters
            if (_tab.SelectedTab == _pgTables)
            {
                // get table/view name
                var table = _treeTables.SelectedNode.Tag as DataTable;
                dt.TableName = table.TableName;

                // get view parameters if necessary
                var parms = OleDbSchema.GetTableParameters(table);
                if (parms != null && parms.Count > 0)
                {
                    var dlg = new ParametersDialog(parms);
                    dlg.Font = Font;
                    if (dlg.ShowDialog(this) != DialogResult.OK)
                    {
                        return;
                    }
                }
            }

            // get data
            try
            {
                using (var da = new OleDbDataAdapter(SelectStatement, ConnectionString))
                {
                    // get data
                    da.Fill(0, MAX_PREVIEW_RECORDS, dt);

                    // show the data
                    using (var dlg = new DataPreviewDialog(dt, Font, Size))
                    {
                        dlg.ShowDialog(this);
                    }
                }
            }
            catch (Exception x)
            {
                Warning(Properties.Resources.ErrGettingData, x.Message);
            }
        }

        // enable/disable buttons
        void UpdateUI()
        {
            // enable sql builder button if we have some tables
            _btnSqlBuilder.Enabled = _treeTables.Nodes.Count > 0;

            // enable data preview if we a select statement
            _btnPreviewData.Enabled = !string.IsNullOrEmpty(SelectStatement);
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Brazil Brazil
Software Architect/Developer with several years experience creating and delivering software.

Full-stack Web development (including React, Firebase, TypeScript, HTML, CSS), Entity Framework, C#, MS SQL Server.

Passionate about new technologies and always keen to learn new things as well as improve on existing skills.

Comments and Discussions