diff --git a/AddMenuItem.Designer.cs b/AddMenuItem.Designer.cs index 122f4b7..39c5d0a 100644 --- a/AddMenuItem.Designer.cs +++ b/AddMenuItem.Designer.cs @@ -28,225 +28,282 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AddMenuItem)); - this.buttonCancel = new System.Windows.Forms.Button(); - this.buttonOK = new System.Windows.Forms.Button(); - this.textName = new System.Windows.Forms.TextBox(); - this.comboContext = new System.Windows.Forms.ComboBox(); - this.checkExecute = new System.Windows.Forms.CheckBox(); - this.checkConfirm = new System.Windows.Forms.CheckBox(); - this.textPath = new System.Windows.Forms.TextBox(); - this.labelName = new System.Windows.Forms.Label(); - this.label1 = new System.Windows.Forms.Label(); - this.radioPath = new System.Windows.Forms.RadioButton(); - this.radioScript = new System.Windows.Forms.RadioButton(); - this.buttonOpen = new System.Windows.Forms.Button(); - this.openFileDialog = new System.Windows.Forms.OpenFileDialog(); - this.labelVersion = new System.Windows.Forms.Label(); - this.SuspendLayout(); - // - // buttonCancel - // - this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.buttonCancel.Location = new System.Drawing.Point(682, 425); - this.buttonCancel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.buttonCancel.Name = "buttonCancel"; - this.buttonCancel.Size = new System.Drawing.Size(112, 35); - this.buttonCancel.TabIndex = 6; - this.buttonCancel.Text = "Cancel"; - this.buttonCancel.UseVisualStyleBackColor = true; - // - // buttonOK - // - this.buttonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK; - this.buttonOK.Enabled = false; - this.buttonOK.Location = new System.Drawing.Point(561, 425); - this.buttonOK.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.buttonOK.Name = "buttonOK"; - this.buttonOK.Size = new System.Drawing.Size(112, 35); - this.buttonOK.TabIndex = 5; - this.buttonOK.Text = "OK"; - this.buttonOK.UseVisualStyleBackColor = true; - // - // textName - // - this.textName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AddMenuItem)); + this.buttonCancel = new System.Windows.Forms.Button(); + this.buttonOK = new System.Windows.Forms.Button(); + this.textName = new System.Windows.Forms.TextBox(); + this.comboContext = new System.Windows.Forms.ComboBox(); + this.checkExecute = new System.Windows.Forms.CheckBox(); + this.checkConfirm = new System.Windows.Forms.CheckBox(); + this.textPath = new System.Windows.Forms.TextBox(); + this.labelName = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.radioPath = new System.Windows.Forms.RadioButton(); + this.radioScript = new System.Windows.Forms.RadioButton(); + this.buttonOpen = new System.Windows.Forms.Button(); + this.openFileDialog = new System.Windows.Forms.OpenFileDialog(); + this.labelVersion = new System.Windows.Forms.Label(); + this.listViewUserDefinedParam = new System.Windows.Forms.ListView(); + this.ParameterName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.ParameterType = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.buttonAddUserDefinedParam = new System.Windows.Forms.Button(); + this.buttonRemoveUserDefinedParam = new System.Windows.Forms.Button(); + this.labelUserDefinedParameters = new System.Windows.Forms.Label(); + this.buttonEditUserDefinedParameter = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // buttonCancel + // + this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(444, 502); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.Size = new System.Drawing.Size(75, 23); + this.buttonCancel.TabIndex = 6; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.UseVisualStyleBackColor = true; + // + // buttonOK + // + this.buttonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.buttonOK.Enabled = false; + this.buttonOK.Location = new System.Drawing.Point(363, 502); + this.buttonOK.Name = "buttonOK"; + this.buttonOK.Size = new System.Drawing.Size(75, 23); + this.buttonOK.TabIndex = 5; + this.buttonOK.Text = "OK"; + this.buttonOK.UseVisualStyleBackColor = true; + // + // textName + // + this.textName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.textName.Location = new System.Drawing.Point(132, 18); - this.textName.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.textName.Name = "textName"; - this.textName.Size = new System.Drawing.Size(661, 26); - this.textName.TabIndex = 0; - this.textName.TextChanged += new System.EventHandler(this.textName_TextChanged); - // - // comboContext - // - this.comboContext.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this.textName.Location = new System.Drawing.Point(88, 12); + this.textName.Name = "textName"; + this.textName.Size = new System.Drawing.Size(431, 20); + this.textName.TabIndex = 0; + this.textName.TextChanged += new System.EventHandler(this.textName_TextChanged); + // + // comboContext + // + this.comboContext.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.comboContext.FormattingEnabled = true; - this.comboContext.Items.AddRange(new object[] { - "All", - "Server", - "Server/DatabasesFolder", - "Server/Database", - "Server/Database/Table", - "Server/Database/UserTablesFolder", - "Server/Database/View", - "Server/Database/StoredProcedure", - "Server/Database/StoredProceduresFolder", - "Server/JobServer", - "Server/JobServer/JobsFolder", - "Server/JobServer/Job"}); - this.comboContext.Location = new System.Drawing.Point(132, 58); - this.comboContext.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.comboContext.Name = "comboContext"; - this.comboContext.Size = new System.Drawing.Size(661, 28); - this.comboContext.TabIndex = 1; - // - // checkExecute - // - this.checkExecute.AutoSize = true; - this.checkExecute.Location = new System.Drawing.Point(132, 100); - this.checkExecute.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.checkExecute.Name = "checkExecute"; - this.checkExecute.Size = new System.Drawing.Size(93, 24); - this.checkExecute.TabIndex = 2; - this.checkExecute.Text = "Execute"; - this.checkExecute.UseVisualStyleBackColor = true; - this.checkExecute.CheckedChanged += new System.EventHandler(this.checkExecute_CheckedChanged); - // - // checkConfirm - // - this.checkConfirm.AutoSize = true; - this.checkConfirm.Location = new System.Drawing.Point(132, 135); - this.checkConfirm.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.checkConfirm.Name = "checkConfirm"; - this.checkConfirm.Size = new System.Drawing.Size(200, 24); - this.checkConfirm.TabIndex = 3; - this.checkConfirm.Text = "Confirm before execute"; - this.checkConfirm.UseVisualStyleBackColor = true; - this.checkConfirm.CheckedChanged += new System.EventHandler(this.checkConfirm_CheckedChanged); - // - // textPath - // - this.textPath.AcceptsReturn = true; - this.textPath.AcceptsTab = true; - this.textPath.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + this.comboContext.FormattingEnabled = true; + this.comboContext.Location = new System.Drawing.Point(88, 38); + this.comboContext.Name = "comboContext"; + this.comboContext.Size = new System.Drawing.Size(431, 21); + this.comboContext.TabIndex = 1; + // + // checkExecute + // + this.checkExecute.AutoSize = true; + this.checkExecute.Location = new System.Drawing.Point(88, 65); + this.checkExecute.Name = "checkExecute"; + this.checkExecute.Size = new System.Drawing.Size(65, 17); + this.checkExecute.TabIndex = 2; + this.checkExecute.Text = "Execute"; + this.checkExecute.UseVisualStyleBackColor = true; + this.checkExecute.CheckedChanged += new System.EventHandler(this.checkExecute_CheckedChanged); + // + // checkConfirm + // + this.checkConfirm.AutoSize = true; + this.checkConfirm.Location = new System.Drawing.Point(88, 88); + this.checkConfirm.Name = "checkConfirm"; + this.checkConfirm.Size = new System.Drawing.Size(135, 17); + this.checkConfirm.TabIndex = 3; + this.checkConfirm.Text = "Confirm before execute"; + this.checkConfirm.UseVisualStyleBackColor = true; + this.checkConfirm.CheckedChanged += new System.EventHandler(this.checkConfirm_CheckedChanged); + // + // textPath + // + this.textPath.AcceptsReturn = true; + this.textPath.AcceptsTab = true; + this.textPath.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.textPath.Location = new System.Drawing.Point(132, 206); - this.textPath.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.textPath.Multiline = true; - this.textPath.Name = "textPath"; - this.textPath.ScrollBars = System.Windows.Forms.ScrollBars.Both; - this.textPath.Size = new System.Drawing.Size(661, 207); - this.textPath.TabIndex = 4; - this.textPath.Text = resources.GetString("textPath.Text"); - this.textPath.WordWrap = false; - this.textPath.TextChanged += new System.EventHandler(this.textPath_TextChanged); - // - // labelName - // - this.labelName.AutoSize = true; - this.labelName.Location = new System.Drawing.Point(18, 23); - this.labelName.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.labelName.Name = "labelName"; - this.labelName.Size = new System.Drawing.Size(51, 20); - this.labelName.TabIndex = 7; - this.labelName.Text = "Name"; - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(18, 63); - this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(64, 20); - this.label1.TabIndex = 8; - this.label1.Text = "Context"; - // - // radioPath - // - this.radioPath.AutoSize = true; - this.radioPath.Location = new System.Drawing.Point(219, 171); - this.radioPath.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.radioPath.Name = "radioPath"; - this.radioPath.Size = new System.Drawing.Size(67, 24); - this.radioPath.TabIndex = 10; - this.radioPath.Text = "Path"; - this.radioPath.UseVisualStyleBackColor = true; - this.radioPath.CheckedChanged += new System.EventHandler(this.radioPath_CheckedChanged); - // - // radioScript - // - this.radioScript.AutoSize = true; - this.radioScript.Checked = true; - this.radioScript.Location = new System.Drawing.Point(132, 171); - this.radioScript.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.radioScript.Name = "radioScript"; - this.radioScript.Size = new System.Drawing.Size(75, 24); - this.radioScript.TabIndex = 11; - this.radioScript.TabStop = true; - this.radioScript.Text = "Script"; - this.radioScript.UseVisualStyleBackColor = true; - this.radioScript.CheckedChanged += new System.EventHandler(this.radioScript_CheckedChanged); - // - // buttonOpen - // - this.buttonOpen.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.buttonOpen.Location = new System.Drawing.Point(758, 166); - this.buttonOpen.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.buttonOpen.Name = "buttonOpen"; - this.buttonOpen.Size = new System.Drawing.Size(38, 35); - this.buttonOpen.TabIndex = 12; - this.buttonOpen.Text = "..."; - this.buttonOpen.UseVisualStyleBackColor = true; - this.buttonOpen.Click += new System.EventHandler(this.buttonOpen_Click); - // - // openFileDialog - // - this.openFileDialog.DefaultExt = "sql"; - this.openFileDialog.FileName = "openFileDialog"; - this.openFileDialog.Filter = "SQL Server files (*.sql)|*.sql"; - // - // labelVersion - // - this.labelVersion.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.labelVersion.AutoSize = true; - this.labelVersion.Location = new System.Drawing.Point(18, 432); - this.labelVersion.Name = "labelVersion"; - this.labelVersion.Size = new System.Drawing.Size(44, 20); - this.labelVersion.TabIndex = 13; - this.labelVersion.Text = "0.0.0"; - // - // AddMenuItem - // - this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(813, 478); - this.Controls.Add(this.labelVersion); - this.Controls.Add(this.buttonOpen); - this.Controls.Add(this.radioScript); - this.Controls.Add(this.radioPath); - this.Controls.Add(this.label1); - this.Controls.Add(this.labelName); - this.Controls.Add(this.textPath); - this.Controls.Add(this.checkConfirm); - this.Controls.Add(this.checkExecute); - this.Controls.Add(this.comboContext); - this.Controls.Add(this.textName); - this.Controls.Add(this.buttonOK); - this.Controls.Add(this.buttonCancel); - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); - this.Name = "AddMenuItem"; - this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = "SSMS Object Explorer Menu | New menu item"; - this.ResumeLayout(false); - this.PerformLayout(); + this.textPath.Location = new System.Drawing.Point(89, 284); + this.textPath.Multiline = true; + this.textPath.Name = "textPath"; + this.textPath.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.textPath.Size = new System.Drawing.Size(431, 210); + this.textPath.TabIndex = 4; + this.textPath.Text = resources.GetString("textPath.Text"); + this.textPath.WordWrap = false; + this.textPath.TextChanged += new System.EventHandler(this.textPath_TextChanged); + // + // labelName + // + this.labelName.AutoSize = true; + this.labelName.Location = new System.Drawing.Point(12, 15); + this.labelName.Name = "labelName"; + this.labelName.Size = new System.Drawing.Size(35, 13); + this.labelName.TabIndex = 7; + this.labelName.Text = "Name"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 41); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(43, 13); + this.label1.TabIndex = 8; + this.label1.Text = "Context"; + // + // radioPath + // + this.radioPath.AutoSize = true; + this.radioPath.Location = new System.Drawing.Point(146, 261); + this.radioPath.Name = "radioPath"; + this.radioPath.Size = new System.Drawing.Size(47, 17); + this.radioPath.TabIndex = 10; + this.radioPath.Text = "Path"; + this.radioPath.UseVisualStyleBackColor = true; + this.radioPath.CheckedChanged += new System.EventHandler(this.radioPath_CheckedChanged); + // + // radioScript + // + this.radioScript.AutoSize = true; + this.radioScript.Checked = true; + this.radioScript.Location = new System.Drawing.Point(88, 261); + this.radioScript.Name = "radioScript"; + this.radioScript.Size = new System.Drawing.Size(52, 17); + this.radioScript.TabIndex = 11; + this.radioScript.TabStop = true; + this.radioScript.Text = "Script"; + this.radioScript.UseVisualStyleBackColor = true; + this.radioScript.CheckedChanged += new System.EventHandler(this.radioScript_CheckedChanged); + // + // buttonOpen + // + this.buttonOpen.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonOpen.Location = new System.Drawing.Point(495, 255); + this.buttonOpen.Name = "buttonOpen"; + this.buttonOpen.Size = new System.Drawing.Size(25, 23); + this.buttonOpen.TabIndex = 12; + this.buttonOpen.Text = "..."; + this.buttonOpen.UseVisualStyleBackColor = true; + this.buttonOpen.Click += new System.EventHandler(this.buttonOpen_Click); + // + // openFileDialog + // + this.openFileDialog.DefaultExt = "sql"; + this.openFileDialog.FileName = "openFileDialog"; + this.openFileDialog.Filter = "SQL Server files (*.sql)|*.sql"; + // + // labelVersion + // + this.labelVersion.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.labelVersion.AutoSize = true; + this.labelVersion.Location = new System.Drawing.Point(12, 510); + this.labelVersion.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); + this.labelVersion.Name = "labelVersion"; + this.labelVersion.Size = new System.Drawing.Size(31, 13); + this.labelVersion.TabIndex = 13; + this.labelVersion.Text = "0.0.0"; + // + // listViewUserDefinedParam + // + this.listViewUserDefinedParam.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listViewUserDefinedParam.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.ParameterName, + this.ParameterType}); + this.listViewUserDefinedParam.HideSelection = false; + this.listViewUserDefinedParam.Location = new System.Drawing.Point(88, 135); + this.listViewUserDefinedParam.Name = "listViewUserDefinedParam"; + this.listViewUserDefinedParam.Size = new System.Drawing.Size(350, 120); + this.listViewUserDefinedParam.TabIndex = 15; + this.listViewUserDefinedParam.UseCompatibleStateImageBehavior = false; + this.listViewUserDefinedParam.View = System.Windows.Forms.View.Details; + this.listViewUserDefinedParam.SelectedIndexChanged += new System.EventHandler(this.listViewUserDefinedParam_SelectedIndexChanged); + // + // ParameterName + // + this.ParameterName.Text = "Parameter name"; + this.ParameterName.Width = 197; + // + // ParameterType + // + this.ParameterType.Text = "Parameter type"; + this.ParameterType.Width = 109; + // + // buttonAddUserDefinedParam + // + this.buttonAddUserDefinedParam.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonAddUserDefinedParam.Location = new System.Drawing.Point(444, 135); + this.buttonAddUserDefinedParam.Name = "buttonAddUserDefinedParam"; + this.buttonAddUserDefinedParam.Size = new System.Drawing.Size(75, 23); + this.buttonAddUserDefinedParam.TabIndex = 16; + this.buttonAddUserDefinedParam.Text = "Add"; + this.buttonAddUserDefinedParam.UseVisualStyleBackColor = true; + this.buttonAddUserDefinedParam.Click += new System.EventHandler(this.buttonAddUserDefinedParam_Click); + // + // buttonRemoveUserDefinedParam + // + this.buttonRemoveUserDefinedParam.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonRemoveUserDefinedParam.Enabled = false; + this.buttonRemoveUserDefinedParam.Location = new System.Drawing.Point(445, 193); + this.buttonRemoveUserDefinedParam.Name = "buttonRemoveUserDefinedParam"; + this.buttonRemoveUserDefinedParam.Size = new System.Drawing.Size(75, 23); + this.buttonRemoveUserDefinedParam.TabIndex = 18; + this.buttonRemoveUserDefinedParam.Text = "Remove"; + this.buttonRemoveUserDefinedParam.UseVisualStyleBackColor = true; + this.buttonRemoveUserDefinedParam.Click += new System.EventHandler(this.buttonRemoveUserDefinedParam_Click); + // + // labelUserDefinedParameters + // + this.labelUserDefinedParameters.AutoSize = true; + this.labelUserDefinedParameters.Location = new System.Drawing.Point(91, 117); + this.labelUserDefinedParameters.Name = "labelUserDefinedParameters"; + this.labelUserDefinedParameters.Size = new System.Drawing.Size(154, 13); + this.labelUserDefinedParameters.TabIndex = 19; + this.labelUserDefinedParameters.Text = "List of user-defined parameters:"; + // + // buttonEditUserDefinedParameter + // + this.buttonEditUserDefinedParameter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonEditUserDefinedParameter.Enabled = false; + this.buttonEditUserDefinedParameter.Location = new System.Drawing.Point(444, 164); + this.buttonEditUserDefinedParameter.Name = "buttonEditUserDefinedParameter"; + this.buttonEditUserDefinedParameter.Size = new System.Drawing.Size(75, 23); + this.buttonEditUserDefinedParameter.TabIndex = 20; + this.buttonEditUserDefinedParameter.Text = "Edit"; + this.buttonEditUserDefinedParameter.UseVisualStyleBackColor = true; + this.buttonEditUserDefinedParameter.Click += new System.EventHandler(this.buttonEditUserDefinedParam_Click); + // + // AddMenuItem + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(531, 540); + this.Controls.Add(this.buttonEditUserDefinedParameter); + this.Controls.Add(this.labelUserDefinedParameters); + this.Controls.Add(this.buttonRemoveUserDefinedParam); + this.Controls.Add(this.buttonAddUserDefinedParam); + this.Controls.Add(this.listViewUserDefinedParam); + this.Controls.Add(this.labelVersion); + this.Controls.Add(this.buttonOpen); + this.Controls.Add(this.radioScript); + this.Controls.Add(this.radioPath); + this.Controls.Add(this.label1); + this.Controls.Add(this.labelName); + this.Controls.Add(this.textPath); + this.Controls.Add(this.checkConfirm); + this.Controls.Add(this.checkExecute); + this.Controls.Add(this.comboContext); + this.Controls.Add(this.textName); + this.Controls.Add(this.buttonOK); + this.Controls.Add(this.buttonCancel); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "AddMenuItem"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "SSMS Object Explorer Menu | New menu item"; + this.ResumeLayout(false); + this.PerformLayout(); } @@ -266,5 +323,12 @@ private void InitializeComponent() private System.Windows.Forms.Button buttonOpen; private System.Windows.Forms.OpenFileDialog openFileDialog; private System.Windows.Forms.Label labelVersion; - } + private System.Windows.Forms.ListView listViewUserDefinedParam; + private System.Windows.Forms.ColumnHeader ParameterName; + private System.Windows.Forms.ColumnHeader ParameterType; + private System.Windows.Forms.Button buttonAddUserDefinedParam; + private System.Windows.Forms.Button buttonRemoveUserDefinedParam; + private System.Windows.Forms.Label labelUserDefinedParameters; + private System.Windows.Forms.Button buttonEditUserDefinedParameter; + } } \ No newline at end of file diff --git a/AddMenuItem.cs b/AddMenuItem.cs index f8ca27e..c1a9399 100644 --- a/AddMenuItem.cs +++ b/AddMenuItem.cs @@ -1,5 +1,9 @@ -using SSMSObjectExplorerMenu.objects; +using SSMSObjectExplorerMenu.enums; +using SSMSObjectExplorerMenu.extensions; +using SSMSObjectExplorerMenu.objects; using System; +using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Windows.Forms; using MenuItem = SSMSObjectExplorerMenu.objects.MenuItem; @@ -13,16 +17,24 @@ public AddMenuItem(NodeInfo nodeInfo) InitializeComponent(); labelVersion.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + comboContext.Text = nodeInfo.UrnPath; - this.ActiveControl = textName; + + this.comboContext.DataSource = + Enum.GetValues(typeof(MenuItemContext)) + .Cast() + .Select(ctx => new { Displayed = ctx.ToStringDescription(), Value = ctx }).ToList(); + this.comboContext.DisplayMember = nameof(ComboBoxItem.Displayed); + this.comboContext.ValueMember = nameof(ComboBoxItem.Value); + + this.ActiveControl = textName; buttonOpen.Visible = false; } public MenuItem GetMenuItem() { - return new MenuItem(true, comboContext.Text, textName.Text, textPath.Text, checkExecute.Checked, checkConfirm.Checked); + return new MenuItem(true, (MenuItemContext)comboContext.SelectedValue, textName.Text, textPath.Text, checkExecute.Checked, checkConfirm.Checked, listViewUserDefinedParam.GetUserDefinedParams()); } - private void textName_TextChanged(object sender, EventArgs e) { @@ -36,8 +48,7 @@ private void textPath_TextChanged(object sender, EventArgs e) private void ValidateInputs() { - if (comboContext.Text.Trim().Length > 0 - && textName.Text.Trim().Length > 0 + if (textName.Text.Trim().Length > 0 && textPath.Text.Trim().Length > 0) { buttonOK.Enabled = true; @@ -86,5 +97,59 @@ private void checkExecute_CheckedChanged(object sender, EventArgs e) checkConfirm.Checked = false; } } - } + + private void buttonAddUserDefinedParam_Click(object sender, EventArgs e) + { + var addDialog = new AddUserDefinedParameter(GetArgumentNamesInUse()); + if (addDialog.ShowDialog() == DialogResult.OK) + { + var newParam = addDialog.Parameter; + var newListViewItem = new ListViewItem { Text = newParam.Name, Tag = newParam }; + newListViewItem.SubItems.Add(new ListViewItem.ListViewSubItem { Text = newParam.Type.ToStringDescription() }); + listViewUserDefinedParam.Items.Add(newListViewItem); + } + } + + private void buttonEditUserDefinedParam_Click(object sender, EventArgs e) + { + var selectedItem = this.listViewUserDefinedParam.GetSelectedItems().SingleOrDefault(); + if (selectedItem != null) + { + var editDialog = new AddUserDefinedParameter(GetArgumentNamesInUse(), true, (UserDefinedParameter)selectedItem.Tag); + if (editDialog.ShowDialog() == DialogResult.OK) + { + var editedParam = editDialog.Parameter; + selectedItem.Text = editedParam.Name; + selectedItem.SubItems[1].Text = Enum.GetName(typeof(UserDefinedParameterType), editedParam.Type); + selectedItem.Tag = editedParam; + } + } + } + + private void buttonRemoveUserDefinedParam_Click(object sender, EventArgs e) + { + var selectedItems = this.listViewUserDefinedParam.GetSelectedItems(); + if (selectedItems.Any() && + DialogResult.Yes == MessageBox.Show("Are you sure?", "Delete parameter(s)", MessageBoxButtons.YesNo, MessageBoxIcon.Warning)) + { + foreach (var item in selectedItems) + { + this.listViewUserDefinedParam.Items.Remove(item); + } + } + } + + private void listViewUserDefinedParam_SelectedIndexChanged(object sender, EventArgs e) + { + // In case of removing items, a minimal delay is needed to work with the state of the listview after the item has been removed. + this.BeginInvoke((Action)(() => + { + var selectedItemsCount = this.listViewUserDefinedParam.GetSelectedItems().Count(); + this.buttonEditUserDefinedParameter.Enabled = selectedItemsCount == 1; + this.buttonRemoveUserDefinedParam.Enabled = selectedItemsCount > 0; + })); + } + + private IEnumerable GetArgumentNamesInUse() => this.listViewUserDefinedParam.Items.Cast().Select(item => item.Text).Concat(Utils.ParametersFromContext); + } } diff --git a/AddUserDefinedParameter.Designer.cs b/AddUserDefinedParameter.Designer.cs new file mode 100644 index 0000000..eae42a6 --- /dev/null +++ b/AddUserDefinedParameter.Designer.cs @@ -0,0 +1,197 @@ +namespace SSMSObjectExplorerMenu +{ + partial class AddUserDefinedParameter + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AddUserDefinedParameter)); + this.textBoxParameterName = new System.Windows.Forms.TextBox(); + this.comboBoxParameterType = new System.Windows.Forms.ComboBox(); + this.labelParameterName = new System.Windows.Forms.Label(); + this.labelParameterType = new System.Windows.Forms.Label(); + this.buttonSave = new System.Windows.Forms.Button(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.listViewCustomList = new System.Windows.Forms.ListView(); + this.buttonAddCustomList = new System.Windows.Forms.Button(); + this.buttonRemoveCustomList = new System.Windows.Forms.Button(); + this.labelCustomList = new System.Windows.Forms.Label(); + this.panelCustomList = new System.Windows.Forms.Panel(); + this.panelCustomList.SuspendLayout(); + this.SuspendLayout(); + // + // textBoxParameterName + // + this.textBoxParameterName.Location = new System.Drawing.Point(103, 14); + this.textBoxParameterName.Name = "textBoxParameterName"; + this.textBoxParameterName.Size = new System.Drawing.Size(170, 20); + this.textBoxParameterName.TabIndex = 0; + // + // comboBoxParameterType + // + this.comboBoxParameterType.FormattingEnabled = true; + this.comboBoxParameterType.Location = new System.Drawing.Point(103, 56); + this.comboBoxParameterType.Name = "comboBoxParameterType"; + this.comboBoxParameterType.Size = new System.Drawing.Size(170, 21); + this.comboBoxParameterType.TabIndex = 1; + this.comboBoxParameterType.SelectedValueChanged += new System.EventHandler(this.comboBoxParameterType_SelectedValueChanged); + // + // labelParameterName + // + this.labelParameterName.AutoSize = true; + this.labelParameterName.Location = new System.Drawing.Point(13, 14); + this.labelParameterName.Name = "labelParameterName"; + this.labelParameterName.Size = new System.Drawing.Size(87, 13); + this.labelParameterName.TabIndex = 2; + this.labelParameterName.Text = "Parameter name:"; + // + // labelParameterType + // + this.labelParameterType.AutoSize = true; + this.labelParameterType.Location = new System.Drawing.Point(13, 56); + this.labelParameterType.Name = "labelParameterType"; + this.labelParameterType.Size = new System.Drawing.Size(81, 13); + this.labelParameterType.TabIndex = 3; + this.labelParameterType.Text = "Parameter type:"; + // + // buttonSave + // + this.buttonSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonSave.Location = new System.Drawing.Point(117, 203); + this.buttonSave.Name = "buttonSave"; + this.buttonSave.Size = new System.Drawing.Size(75, 23); + this.buttonSave.TabIndex = 4; + this.buttonSave.Text = "Save"; + this.buttonSave.UseVisualStyleBackColor = true; + this.buttonSave.Click += new System.EventHandler(this.buttonSave_Click); + // + // buttonCancel + // + this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(198, 203); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.Size = new System.Drawing.Size(75, 23); + this.buttonCancel.TabIndex = 5; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.UseVisualStyleBackColor = true; + // + // listViewCustomList + // + this.listViewCustomList.HideSelection = false; + this.listViewCustomList.LabelEdit = true; + this.listViewCustomList.Location = new System.Drawing.Point(0, 18); + this.listViewCustomList.Name = "listViewCustomList"; + this.listViewCustomList.Size = new System.Drawing.Size(228, 79); + this.listViewCustomList.TabIndex = 6; + this.listViewCustomList.UseCompatibleStateImageBehavior = false; + this.listViewCustomList.View = System.Windows.Forms.View.List; + this.listViewCustomList.SelectedIndexChanged += new System.EventHandler(this.listViewCustomList_SelectedIndexChanged); + // + // buttonAddCustomList + // + this.buttonAddCustomList.Location = new System.Drawing.Point(234, 16); + this.buttonAddCustomList.Name = "buttonAddCustomList"; + this.buttonAddCustomList.Size = new System.Drawing.Size(23, 23); + this.buttonAddCustomList.TabIndex = 7; + this.buttonAddCustomList.Text = "+"; + this.buttonAddCustomList.UseVisualStyleBackColor = true; + this.buttonAddCustomList.Click += new System.EventHandler(this.buttonAddCustomList_Click); + // + // buttonRemoveCustomList + // + this.buttonRemoveCustomList.Enabled = false; + this.buttonRemoveCustomList.Location = new System.Drawing.Point(234, 45); + this.buttonRemoveCustomList.Name = "buttonRemoveCustomList"; + this.buttonRemoveCustomList.Size = new System.Drawing.Size(23, 23); + this.buttonRemoveCustomList.TabIndex = 8; + this.buttonRemoveCustomList.Text = "-"; + this.buttonRemoveCustomList.UseVisualStyleBackColor = true; + this.buttonRemoveCustomList.Click += new System.EventHandler(this.buttonRemoveCustomList_Click); + // + // labelCustomList + // + this.labelCustomList.AutoSize = true; + this.labelCustomList.Location = new System.Drawing.Point(3, 2); + this.labelCustomList.Name = "labelCustomList"; + this.labelCustomList.Size = new System.Drawing.Size(75, 13); + this.labelCustomList.TabIndex = 9; + this.labelCustomList.Text = "List of options:"; + // + // panelCustomList + // + this.panelCustomList.Controls.Add(this.labelCustomList); + this.panelCustomList.Controls.Add(this.buttonRemoveCustomList); + this.panelCustomList.Controls.Add(this.buttonAddCustomList); + this.panelCustomList.Controls.Add(this.listViewCustomList); + this.panelCustomList.Location = new System.Drawing.Point(16, 95); + this.panelCustomList.Name = "panelCustomList"; + this.panelCustomList.Size = new System.Drawing.Size(257, 100); + this.panelCustomList.TabIndex = 10; + // + // AddUserDefinedParameter + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.ClientSize = new System.Drawing.Size(285, 238); + this.Controls.Add(this.panelCustomList); + this.Controls.Add(this.buttonCancel); + this.Controls.Add(this.buttonSave); + this.Controls.Add(this.labelParameterType); + this.Controls.Add(this.labelParameterName); + this.Controls.Add(this.comboBoxParameterType); + this.Controls.Add(this.textBoxParameterName); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AddUserDefinedParameter"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Add user-defined parameter..."; + this.panelCustomList.ResumeLayout(false); + this.panelCustomList.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox textBoxParameterName; + private System.Windows.Forms.ComboBox comboBoxParameterType; + private System.Windows.Forms.Label labelParameterName; + private System.Windows.Forms.Label labelParameterType; + private System.Windows.Forms.Button buttonSave; + private System.Windows.Forms.Button buttonCancel; + private System.Windows.Forms.ListView listViewCustomList; + private System.Windows.Forms.Button buttonAddCustomList; + private System.Windows.Forms.Button buttonRemoveCustomList; + private System.Windows.Forms.Label labelCustomList; + private System.Windows.Forms.Panel panelCustomList; + } +} \ No newline at end of file diff --git a/AddUserDefinedParameter.cs b/AddUserDefinedParameter.cs new file mode 100644 index 0000000..da4f417 --- /dev/null +++ b/AddUserDefinedParameter.cs @@ -0,0 +1,131 @@ +using SSMSObjectExplorerMenu.enums; +using SSMSObjectExplorerMenu.extensions; +using SSMSObjectExplorerMenu.objects; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace SSMSObjectExplorerMenu +{ + public partial class AddUserDefinedParameter : Form + { + private IEnumerable _paramNamesInUse; + private ParameterItem _parameter; + + public UserDefinedParameter Parameter + { + get + { + if (!TryValidate(out UserDefinedParameter parameter, out List _) || this.DialogResult != DialogResult.OK) + { + throw new InvalidOperationException("Dialog is in invalid state."); + } + return parameter; + } + } + + public AddUserDefinedParameter(IEnumerable paramNamesInUse, bool edit = false, UserDefinedParameter parameterToEdit = null) + { + if(edit && parameterToEdit is null) + { + throw new ArgumentNullException(nameof(parameterToEdit), $"Parameter '{nameof(parameterToEdit)}' must not be null if patameter '{nameof(edit)}' is set to true."); + } + + InitializeComponent(); + + _parameter = new ParameterItem { Name = string.Empty, Type = UserDefinedParameterType.UniqueIdentifier }; + _paramNamesInUse = !edit ? paramNamesInUse : paramNamesInUse.Except(new[] { parameterToEdit.Name }); + + this.textBoxParameterName.MaxLength = UserDefinedParameter.NAME_MAX_LENGTH; + this.textBoxParameterName.DataBindings.Add(nameof(textBoxParameterName.Text), _parameter, nameof(_parameter.Name), true, DataSourceUpdateMode.OnPropertyChanged); + + this.comboBoxParameterType.DataSource = + Enum.GetValues(typeof(UserDefinedParameterType)) + .Cast() + .Select(type => new ComboBoxItem { Displayed = type.ToStringDescription(), Value = type }).ToList(); + this.comboBoxParameterType.DisplayMember = nameof(ComboBoxItem.Displayed); + this.comboBoxParameterType.ValueMember = nameof(ComboBoxItem.Value); + this.comboBoxParameterType.DataBindings.Add(nameof(comboBoxParameterType.SelectedValue), _parameter, nameof(_parameter.Type), true, DataSourceUpdateMode.OnPropertyChanged); + + if(edit) + { + _parameter.Name = parameterToEdit.Name; + _parameter.Type = parameterToEdit.Type; + this.listViewCustomList.Items.AddRange(parameterToEdit.ValueSetOfCustomList.Select(item => new ListViewItem(item.Value)).ToArray()); + this.Text = "Edit user-defined parameter..."; + } + } + + private void buttonSave_Click(object sender, EventArgs e) + { + if (!TryValidate(out UserDefinedParameter _, out List validationErrors)) + { + var errorMessageBuilder = new StringBuilder(); + validationErrors.ForEach(error => errorMessageBuilder.AppendLine(error)); + MessageBox.Show(errorMessageBuilder.ToString(), "Validation failed", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + this.DialogResult = DialogResult.OK; + } + + private bool TryValidate(out UserDefinedParameter validatedParameter, out List validationErrors) + { + var parameter = BuildParameter(); + validationErrors = !parameter.TryValidate(out IEnumerable errors, _paramNamesInUse) ? errors.ToList() : new List(); + + bool success = !validationErrors.Any(); + validatedParameter = success ? parameter : null; + return success; + } + + private UserDefinedParameter BuildParameter() => new UserDefinedParameter + { + Name = _parameter.Name, + Type = _parameter.Type, + ValueSetOfCustomList = new BindingList( + _parameter.Type == UserDefinedParameterType.CustomList ? + this.listViewCustomList.Items.Cast().Select(item => new StringListItem(item.Text)).ToList() : + new List() + ) + }; + + private void comboBoxParameterType_SelectedValueChanged(object sender, EventArgs e) + { + var selectedValue = comboBoxParameterType.SelectedValue as UserDefinedParameterType?; + if(selectedValue != null) + { + this.panelCustomList.Enabled = selectedValue == UserDefinedParameterType.CustomList; + } + } + + private void buttonAddCustomList_Click(object sender, EventArgs e) + { + this.listViewCustomList.Items.Add("New value (double-click to edit)..."); + } + + private void buttonRemoveCustomList_Click(object sender, EventArgs e) + { + var selectedItems = this.listViewCustomList.Items.Cast().Where(item => item.Selected); + this.listViewCustomList.Items.RemoveRange(selectedItems); + } + private void listViewCustomList_SelectedIndexChanged(object sender, EventArgs e) + { + // In case of removing list items: + // This method is called shortly before the listview item is actually removed. + // So the collection returned by GetSelectedItems() contains also the item to remove. + // A very minor delay is needed to have the correct state of the listview. + this.BeginInvoke((Action)(() => this.buttonRemoveCustomList.Enabled = this.listViewCustomList.GetSelectedItems().Any())); + } + + class ParameterItem + { + public string Name { get; set; } + public UserDefinedParameterType Type { get; set; } + public ISet ValueSetOfCustomList { get; set; } + } + } +} diff --git a/AddUserDefinedParameter.resx b/AddUserDefinedParameter.resx new file mode 100644 index 0000000..d729beb --- /dev/null +++ b/AddUserDefinedParameter.resx @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAA + AACTlZX/hoiJ/5GTlP+PkZL/kJGS/5CRkv+QkZL/kJGS/5CRk/+QkZL/kJGS/5CRkv+PkZL/kZOU/4aI + if+SlJX/goSF/+Pk5P/5+fn/9fX1//X29v/19fb/9fX2//f39f/39/X/9fX2//X29v/19vb/9fX1//n5 + +f/k5OX/g4WG/46Qkf/4+Pj///////////////////////7+/v9XSa7/Vkit//79/f////////7///// + ////////+Pj4/4+Rkv+Nj5D/9/f3///////+/v7//v7+///////9/f3/Khua/ygamP/8/Pz///////7+ + /v/+/v7///////f39/+Oj5H/jY+Q//f4+P///////v7+//7+/v///////f3+/zQjn/8yIZ3//Pz9//// + ///+/v7//v7+///////4+Pj/jpCR/42PkP/3+Pj////////////+/v////////7+/v8yIZ7/MB+c//39 + /f///////v7/////////////+Pj4/46Qkf+Nj5D/9/j4//////////7//v7////////+/v7/MiGe/zAf + nP/9/f3///////7+//////7///////j4+P+OkJH/jpCQ//f39/9XSa//Lh2c/zgnoP82JqD/NiWg/xwJ + lP8cCZT/NiWg/zYmoP83J6D/Lx6c/1ZIr//39/f/j5CR/46QkP/39/f/T0Cr/yUTmP8uHZz/LRub/y0b + m/8bCJT/GwiU/y0bm/8tG5v/Lh2c/yUTmP9OP6v/9/f3/4+Qkf+Nj5D/9/j4//79///7+vz/+/r8//z8 + /f/6+fz/MiCd/zAfnP/5+fv//Pz9//v6/P/7+vz//f3///j4+P+OkJH/jY+Q//f4+P////////////7+ + /////////v7+/zIhnv8xH5z//f39///////+/v/////////////4+Pj/jpCR/42PkP/3+Pj///////7+ + /v/+/v7///////39/v80Ip7/MiGd//z8/f///////v7+//7+/v//////+Pj4/46Qkf+Nj5D/9/f3//// + ///+/v7//v7+///////9/f7/Kxyb/ykamf/8/P3///////7+/v/+/v7///////f3+P+Oj5H/jpCR//j4 + +P/////////////+/////////v7+/09Aqv9OP6n//f39/////////v/////////////4+Pj/j5CS/4SG + h//o6Oj//f39//n5+f/5+fn/+fn5//n5+f/39/f/9/b3//n5+f/5+vn/+fn5//n5+f/9/f3/6Onp/4SG + h/+OkJH/hYeI/5KTlP+QkZL/kJKS/5CSkv+QkpL/kZOT/5GTk/+QkpL/kJKS/5CSkv+QkZL/kpOU/4WH + iP+Nj5D/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAA== + + + \ No newline at end of file diff --git a/ArgumentControl.Designer.cs b/ArgumentControl.Designer.cs new file mode 100644 index 0000000..a631577 --- /dev/null +++ b/ArgumentControl.Designer.cs @@ -0,0 +1,44 @@ +namespace SSMSObjectExplorerMenu +{ + partial class ArgumentControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // ArgumentControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Name = "ArgumentControl"; + this.ResumeLayout(false); + + } + + #endregion + } +} diff --git a/ArgumentControl.cs b/ArgumentControl.cs new file mode 100644 index 0000000..c325db4 --- /dev/null +++ b/ArgumentControl.cs @@ -0,0 +1,130 @@ +using SSMSObjectExplorerMenu.enums; +using SSMSObjectExplorerMenu.extensions; +using SSMSObjectExplorerMenu.objects; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Windows.Forms; + +namespace SSMSObjectExplorerMenu +{ + public partial class ArgumentControl : UserControl + { + /// + /// Padding of the control in pixels. + /// + private const int PADDING_TOP_BOTTOM = 2; + private const byte UNIQUEIDENTIFIER_LENGTH = 36; + + private Label _labelParameterName; + private Control _valueControl; + + public UserDefinedParameter Parameter { get; private set; } + public string ParameterValueString + { + get + { + if(Parameter.Type == UserDefinedParameterType.Bit) + { + return ((CheckBox)_valueControl).Checked ? "1" : "0"; + } + else return _valueControl.Text; + } + } + + public ArgumentControl(UserDefinedParameter parameter, int width) + { + if(!parameter.TryValidate(out IEnumerable _)) + { + throw new ArgumentException("Parameter validation has failed.", nameof(parameter)); + } + if (width < 0) + { + throw new ArgumentException("Width cannot be negative.", nameof(width)); + } + + Parameter = parameter; + + InitializeComponent(); + + switch (Parameter.Type) + { + case UserDefinedParameterType.UniqueIdentifier: + Init_Uniqueidentifier(); + break; + case UserDefinedParameterType.Nvarchar: + Init_Nvarchar(); + break; + case UserDefinedParameterType.Int: + Init_Int(); + break; + case UserDefinedParameterType.Bit: + Init_Bit(); + break; + case UserDefinedParameterType.CustomList: + Init_CustomList(); + break; + default: + throw new ArgumentException("Parameter type is not known.", nameof(Parameter)); + } + this._labelParameterName = new Label(); + + this.Size = new Size(width, (2 * PADDING_TOP_BOTTOM) + this._labelParameterName.Height + this._valueControl.Height); + + this._labelParameterName.Location = new Point(0, PADDING_TOP_BOTTOM); + this._labelParameterName.Width = this.Width; + this._labelParameterName.Text = $"{Parameter.Name} ({Parameter.Type.ToStringDescription()}):"; + this.Controls.Add(this._labelParameterName); + + this._valueControl.Location = new Point(0, this._labelParameterName.Location.Y + this._labelParameterName.Height); + this._valueControl.Width = this.Size.Width; + this.Controls.Add(this._valueControl); + } + + public bool IsValid() + { + switch (Parameter.Type) + { + case UserDefinedParameterType.UniqueIdentifier: + return Guid.TryParse(_valueControl.Text, out _); + case UserDefinedParameterType.Nvarchar: + return !string.IsNullOrWhiteSpace(_valueControl.Text); + case UserDefinedParameterType.Int: + case UserDefinedParameterType.Bit: + case UserDefinedParameterType.CustomList: + return true; + default: + throw new NotImplementedException($"Validation for parameter type {Parameter.Type} has not been implemented."); + } + } + + private void Init_Uniqueidentifier() + { + var guidTextBox = new TextBox(); + guidTextBox.MaxLength = UNIQUEIDENTIFIER_LENGTH; + guidTextBox.Text = string.Empty; + + _valueControl = guidTextBox; + } + + private void Init_Nvarchar() + { + var textBox = new TextBox(); + textBox.Text = string.Empty; + + _valueControl = textBox; + } + + private void Init_Int() => _valueControl = new NumericUpDown() { Minimum = int.MinValue, Maximum = int.MaxValue }; + + private void Init_Bit() => _valueControl = new CheckBox(); + + private void Init_CustomList() + { + var comboBox = new ComboBox(); + comboBox.DataSource = Parameter.ValueSetOfCustomList; + + _valueControl = comboBox; + } + } +} diff --git a/ArgumentControl.resx b/ArgumentControl.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/ArgumentControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/EnterUserDefinedArguments.Designer.cs b/EnterUserDefinedArguments.Designer.cs new file mode 100644 index 0000000..12d3268 --- /dev/null +++ b/EnterUserDefinedArguments.Designer.cs @@ -0,0 +1,115 @@ +namespace SSMSObjectExplorerMenu +{ + partial class EnterUserDefinedArguments + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(EnterUserDefinedArguments)); + this.label1 = new System.Windows.Forms.Label(); + this.flowLayoutPanelArguments = new System.Windows.Forms.FlowLayoutPanel(); + this.buttonOK = new System.Windows.Forms.Button(); + this.dialogPage1 = new Microsoft.VisualStudio.Shell.DialogPage(); + this.buttonCancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(222, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Please enter values for the parameters below."; + // + // flowLayoutPanelArguments + // + this.flowLayoutPanelArguments.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.flowLayoutPanelArguments.AutoScroll = true; + this.flowLayoutPanelArguments.AutoSize = true; + this.flowLayoutPanelArguments.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; + this.flowLayoutPanelArguments.Location = new System.Drawing.Point(15, 26); + this.flowLayoutPanelArguments.Name = "flowLayoutPanelArguments"; + this.flowLayoutPanelArguments.Size = new System.Drawing.Size(300, 50); + this.flowLayoutPanelArguments.TabIndex = 1; + this.flowLayoutPanelArguments.WrapContents = false; + // + // buttonOK + // + this.buttonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonOK.Location = new System.Drawing.Point(159, 82); + this.buttonOK.Name = "buttonOK"; + this.buttonOK.Size = new System.Drawing.Size(75, 23); + this.buttonOK.TabIndex = 2; + this.buttonOK.Text = "OK"; + this.buttonOK.UseVisualStyleBackColor = true; + this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click); + // + // buttonCancel + // + this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.buttonCancel.Location = new System.Drawing.Point(240, 82); + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.Size = new System.Drawing.Size(75, 23); + this.buttonCancel.TabIndex = 3; + this.buttonCancel.Text = "Cancel"; + this.buttonCancel.UseVisualStyleBackColor = true; + // + // EnterUserDefinedArguments + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.CancelButton = this.buttonCancel; + this.ClientSize = new System.Drawing.Size(329, 111); + this.Controls.Add(this.buttonCancel); + this.Controls.Add(this.buttonOK); + this.Controls.Add(this.flowLayoutPanelArguments); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EnterUserDefinedArguments"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Enter script arguments..."; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanelArguments; + private System.Windows.Forms.Button buttonOK; + private Microsoft.VisualStudio.Shell.DialogPage dialogPage1; + private System.Windows.Forms.Button buttonCancel; + } +} \ No newline at end of file diff --git a/EnterUserDefinedArguments.cs b/EnterUserDefinedArguments.cs new file mode 100644 index 0000000..49b68e4 --- /dev/null +++ b/EnterUserDefinedArguments.cs @@ -0,0 +1,51 @@ +using SSMSObjectExplorerMenu.extensions; +using SSMSObjectExplorerMenu.objects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace SSMSObjectExplorerMenu +{ + public partial class EnterUserDefinedArguments : Form + { + private readonly ArgumentControl[] _argumentControls; + + public IEnumerable UserDefinedArguments { + get + { + if(!TryValidate(out IEnumerable _) || this.DialogResult != DialogResult.OK) + { + throw new InvalidOperationException("Dialog is in invalid state."); + } + return _argumentControls.Select(ac => ac.ToUserDefinedArgument()); + } + } + + public EnterUserDefinedArguments(IEnumerable parameters) + { + InitializeComponent(); + + int argumentControlWidth = this.flowLayoutPanelArguments.ClientSize.Width; + _argumentControls = parameters.Select(p => new ArgumentControl(p, argumentControlWidth)).ToArray(); + this.flowLayoutPanelArguments.Controls.AddRange(_argumentControls); + } + + private void buttonOK_Click(object sender, EventArgs e) + { + if(!TryValidate(out IEnumerable invalidArguments)) + { + MessageBox.Show(this, $"The following parameters have invalid values: {string.Join(", ", invalidArguments)}.", "Validation failed", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + this.DialogResult = DialogResult.OK; + } + + private bool TryValidate(out IEnumerable invalidArguments) + { + invalidArguments = _argumentControls.Where(ac => !ac.IsValid()).Select(ac => ac.Parameter.Name); + return !invalidArguments.Any(); + } + } +} diff --git a/EnterUserDefinedArguments.resx b/EnterUserDefinedArguments.resx new file mode 100644 index 0000000..a92f93f --- /dev/null +++ b/EnterUserDefinedArguments.resx @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAA + AACTlZX/hoiJ/5GTlP+PkZL/kJGS/5CRkv+QkZL/kJGS/5CRk/+QkZL/kJGS/5CRkv+PkZL/kZOU/4aI + if+SlJX/goSF/+Pk5P/5+fn/9fX1//X29v/19fb/9fX2//f39f/39/X/9fX2//X29v/19vb/9fX1//n5 + +f/k5OX/g4WG/46Qkf/4+Pj///////////////////////7+/v9XSa7/Vkit//79/f////////7///// + ////////+Pj4/4+Rkv+Nj5D/9/f3///////+/v7//v7+///////9/f3/Khua/ygamP/8/Pz///////7+ + /v/+/v7///////f39/+Oj5H/jY+Q//f4+P///////v7+//7+/v///////f3+/zQjn/8yIZ3//Pz9//// + ///+/v7//v7+///////4+Pj/jpCR/42PkP/3+Pj////////////+/v////////7+/v8yIZ7/MB+c//39 + /f///////v7/////////////+Pj4/46Qkf+Nj5D/9/j4//////////7//v7////////+/v7/MiGe/zAf + nP/9/f3///////7+//////7///////j4+P+OkJH/jpCQ//f39/9XSa//Lh2c/zgnoP82JqD/NiWg/xwJ + lP8cCZT/NiWg/zYmoP83J6D/Lx6c/1ZIr//39/f/j5CR/46QkP/39/f/T0Cr/yUTmP8uHZz/LRub/y0b + m/8bCJT/GwiU/y0bm/8tG5v/Lh2c/yUTmP9OP6v/9/f3/4+Qkf+Nj5D/9/j4//79///7+vz/+/r8//z8 + /f/6+fz/MiCd/zAfnP/5+fv//Pz9//v6/P/7+vz//f3///j4+P+OkJH/jY+Q//f4+P////////////7+ + /////////v7+/zIhnv8xH5z//f39///////+/v/////////////4+Pj/jpCR/42PkP/3+Pj///////7+ + /v/+/v7///////39/v80Ip7/MiGd//z8/f///////v7+//7+/v//////+Pj4/46Qkf+Nj5D/9/f3//// + ///+/v7//v7+///////9/f7/Kxyb/ykamf/8/P3///////7+/v/+/v7///////f3+P+Oj5H/jpCR//j4 + +P/////////////+/////////v7+/09Aqv9OP6n//f39/////////v/////////////4+Pj/j5CS/4SG + h//o6Oj//f39//n5+f/5+fn/+fn5//n5+f/39/f/9/b3//n5+f/5+vn/+fn5//n5+f/9/f3/6Onp/4SG + h/+OkJH/hYeI/5KTlP+QkZL/kJKS/5CSkv+QkpL/kZOT/5GTk/+QkpL/kJKS/5CSkv+QkZL/kpOU/4WH + iP+Nj5D/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAA== + + + \ No newline at end of file diff --git a/EnumConverter.cs b/EnumConverter.cs new file mode 100644 index 0000000..9e2445f --- /dev/null +++ b/EnumConverter.cs @@ -0,0 +1,39 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Reflection; + +namespace SSMSObjectExplorerMenu +{ + public abstract class EnumConverter : EnumConverter where T : Enum + { + public EnumConverter() : base(typeof(T)) + { + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if (value is T && destinationType == typeof(string)) + { + var field = typeof(T).GetField(value.ToString()); + var desc = field?.GetCustomAttribute(); + return (desc?.Description) ?? throw new ArgumentException($"Unknown {typeof(T)} value.", nameof(value)); + } + return base.ConvertTo(context, culture, value, destinationType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string val) + { + var targetField = typeof(T).GetFields() + .SingleOrDefault(f => f.GetCustomAttribute()?.Description == val); + return targetField != null ? + Enum.Parse(typeof(T), targetField.Name) : + throw new ArgumentException($"Unknown {typeof(T)} value.", nameof(value)); + } + return base.ConvertFrom(context, culture, value); + } + } +} diff --git a/MenuItemContextConverter.cs b/MenuItemContextConverter.cs new file mode 100644 index 0000000..4a2ff88 --- /dev/null +++ b/MenuItemContextConverter.cs @@ -0,0 +1,8 @@ +using SSMSObjectExplorerMenu.enums; + +namespace SSMSObjectExplorerMenu +{ + public class MenuItemContextConverter : EnumConverter + { + } +} diff --git a/OptionsDialogPage.cs b/OptionsDialogPage.cs index bcebbde..8eaae5d 100644 --- a/OptionsDialogPage.cs +++ b/OptionsDialogPage.cs @@ -1,7 +1,14 @@ using Microsoft.VisualStudio.Shell; +using SSMSObjectExplorerMenu.extensions; using SSMSObjectExplorerMenu.objects; +using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; +using System.Text; +using MessageBox = System.Windows.Forms.MessageBox; +using MessageBoxButtons = System.Windows.Forms.MessageBoxButtons; +using MessageBoxIcon = System.Windows.Forms.MessageBoxIcon; namespace SSMSObjectExplorerMenu { @@ -90,5 +97,38 @@ public override void SaveSettingsToStorage() base.SaveSettingsToStorage(); } - } + protected override void OnApply(PageApplyEventArgs e) + { + if(!TryValidate(out List validationErrors)) + { + ShowValidationErrorDialog(validationErrors); + e.ApplyBehavior = ApplyKind.CancelNoNavigate; + } + else base.OnApply(e); // Calls SaveSettingsToStorage + } + + private bool TryValidate(out List validationErrors) + { + validationErrors = MenuItems.Select(mi => mi.TryValidate(out IEnumerable errors) ? null : errors) + .Where(e => e != null) + .SelectMany(e => e) + .ToList(); + return !validationErrors.Any(); + } + + private void ShowValidationErrorDialog(List validationErrors) + { + if(validationErrors.Any()) + { + var builder = new StringBuilder(); + + builder.AppendLine($"One or more validation errors occurred:{Environment.NewLine}"); + foreach (var error in validationErrors.SelectMany(e => e.ErrorMessages, (m, e) => new { m.MenuItemName, ErrorMessage = e})) + { + builder.AppendLine($"Menu item '{error.MenuItemName}': {error.ErrorMessage}"); + } + MessageBox.Show(builder.ToString(), "SSMS Object Explorer Menu | Validation failed", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } } diff --git a/README.md b/README.md index fbcd608..cbe6a5a 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Object Explorer Menu is a free, open-source extension for SQL Server Management These menu items can be configured to run either external T-SQL script files or inline T-SQL statements. Upon selection, the extension opens a new query window displaying the script. It also supports tag substitution within scripts and optional automatic execution, streamlining routine database tasks and enhancing productivity. +Adding own parameters to the menu items are also supported. Every menu item can have its own list of them. Their values can be enetered in a dialog before the T-SQL script's execution. + The project homepage is [https://sqlmedic.com](https://sqlmedic.com). [![GitHub release (latest by date)](https://img.shields.io/github/v/release/brink-daniel/ssms-object-explorer-menu)](https://github.com/brink-daniel/ssms-object-explorer-menu/releases) @@ -31,9 +33,10 @@ Once the Object Explorer Menu extension is installed, new menu items can be adde 1. Right-click on the node in the Object Explorer where you would like to add a context menu item and select `New` from the `My Scripts` menu. ![Object Explorer](images/ObjectExplorer.png) ![Add Menu Item](images/AddMenuItem.png) + 2. Open the Options dialog window in SSMS `Tools > Options > SQL Server Object Explorer > SSMS Object Explorer Menu` and add new menu items to the collection. Menu items can also be rearranged or removed using the Options dialog. ![Options Dialog](images/Options.png) - + ![Options Dialog](images/Options_parameter.png) ## Settings @@ -57,11 +60,14 @@ The following settings are available for each menu item: * Execute - Automatically run the selected script or tsql statements when the menu item is selected. * Name - Text displayed on the menu item. * Script - Inline tsql statements OR path to script file. +* User-defined parameters - list of custom parameters. They can be interpreted as a custom tag. The main difference is that their substitution values are not taken from the execution context - the user can enter them, before the T-SQL script will be run. ### Text substitution -The following tags are replaced in tsql scripts and statements before execution: +#### Tags of the execution context + +The following tags are replaced in T-SQL scripts and statements before execution: * `{SERVER}` * `{DATABASE}` @@ -76,6 +82,31 @@ The following tags are replaced in tsql scripts and statements before execution: * `{YYYY-MM-DD HH:mm:ss}` * `{OBJECT}` +#### User-defined parameters (tags) + +Every menu item can have a set of additional parameters if needed. They can be defined at the time when the menu item is added and can be edited later in the Options grid. Each of them must have a unique name and a (data) type. + +When adding a parameter, do not wrap its name in curly braces. It will be wrapped automatically when looking for the parameter during text substitution in the T-SQL script. + +* Use form without braces when giving a name for a parameter: `MY_CUSTOM_PARAM`. +* However, wrap the parameter name in braces in the T-SQL script: `{MY_CUSTOM_PARAM}`. + +The following rules are applied on parameter names: + +* A menu item cannot have two parameters having the same name (two names differ only in casing are considered as equal). +* Tag names of the execution context (like `{SERVER}`, `{DATABASE}`, etc.) cannot be used as a custom parameter name. + +A custom parameter can have one of the below types: + +* uniqueidentifier +* int +* nvarchar +* bit +* List of options + +The list of options can't have duplicate elements. + +These user-defined parameters are substituted the same way like the tags of the execution context. #### Example 1 @@ -85,7 +116,7 @@ select , '{DATABASE}' as [database] , '{SCHEMA}' as [schema] , '{TABLE}' as [table] - , '{VIEW}' as [view] + , '{VIEW}' as [view] , '{STORED_PROCEDURE}' as [stored_procedure] , '{FUNCTION}' as [function] , '{JOB}' as [job] @@ -103,7 +134,13 @@ select top 10 from {DATABASE}.{SCHEMA}.{TABLE} ``` +#### Example 3 +```sql +-- {OBJECT_TYPE} and {NAME_FILTER_EXP} are user-defined (custom) parameters +select * from sys.objects +where type_desc = '{OBJECT_TYPE}' and name like '{NAME_FILTER_EXP}'; +``` ## Compatibility diff --git a/SSMSObjectExplorerMenu.cs b/SSMSObjectExplorerMenu.cs index 382f640..167a808 100644 --- a/SSMSObjectExplorerMenu.cs +++ b/SSMSObjectExplorerMenu.cs @@ -3,11 +3,10 @@ using Microsoft.SqlServer.Management.UI.VSIntegration; using Microsoft.SqlServer.Management.UI.VSIntegration.Editors; using Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer; -using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; -using Microsoft.VisualStudio.VCProjectEngine; +using SSMSObjectExplorerMenu.extensions; using SSMSObjectExplorerMenu.objects; using System; using System.Collections.Generic; @@ -119,7 +118,8 @@ private void TreeView_ContextMenuStripChanged(object sender, EventArgs e) continue; } - if (menuItem.Context == "All" || menuItem.Context == nodes[0].UrnPath) + var menuItemContext = menuItem.Context.ToStringDescription(); + if (menuItemContext == "All" || menuItemContext == nodes[0].UrnPath) { MenuItemInstance instance = new MenuItemInstance(menuItem, nodeInfo, nodes[0].InvariantName); @@ -325,23 +325,36 @@ private async Task Menu_ClickAsync(object sender, EventArgs e) script = itemInstance.MenuItem.Script; } + var contextDefinedArgs = new (string Parameter, string Value)[] + { + ("{OBJECT}", itemInstance.Name), + ("{SERVER}", itemInstance.NodeInfo.Server), + ("{DATABASE}", itemInstance.NodeInfo.Database), + ("{TABLE}", itemInstance.NodeInfo.Table), + ("{VIEW}", itemInstance.NodeInfo.View), + ("{STORED_PROCEDURE}", itemInstance.NodeInfo.StoredProcedure), + ("{FUNCTION}", itemInstance.NodeInfo.Function), + ("{SCHEMA}", itemInstance.NodeInfo.Schema), + ("{JOB}", itemInstance.NodeInfo.Job), + ("{YYYY-MM-DD}", DateTime.Now.ToString("yyyy-MM-dd")), + ("{HH:mm:ss}", DateTime.Now.ToString("HH:mm:ss")), + ("{YYYY-MM-DD HH:mm:ss}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")) + }; - script = script - .Replace("{OBJECT}", itemInstance.Name) - .Replace("{SERVER}", itemInstance.NodeInfo.Server) - .Replace("{DATABASE}", itemInstance.NodeInfo.Database) - .Replace("{TABLE}", itemInstance.NodeInfo.Table) - .Replace("{VIEW}", itemInstance.NodeInfo.View) - .Replace("{STORED_PROCEDURE}", itemInstance.NodeInfo.StoredProcedure) - .Replace("{FUNCTION}", itemInstance.NodeInfo.Function) - .Replace("{SCHEMA}", itemInstance.NodeInfo.Schema) - .Replace("{JOB}", itemInstance.NodeInfo.Job) - .Replace("{YYYY-MM-DD}", DateTime.Now.ToString("yyyy-MM-dd")) - .Replace("{HH:mm:ss}", DateTime.Now.ToString("HH:mm:ss")) - .Replace("{YYYY-MM-DD HH:mm:ss}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); - - + IEnumerable<(string Parameter, string Value)> userDefinedArgs = Enumerable.Empty<(string, string)>(); + var userDefinedParams = itemInstance.MenuItem.UserDefinedParameters; + if (userDefinedParams.Any()) + { + var enterArgumentsDialog = new EnterUserDefinedArguments(userDefinedParams); + if (DialogResult.OK == enterArgumentsDialog.ShowDialog()) + { + userDefinedArgs = enterArgumentsDialog.UserDefinedArguments.Select(arg => ($"{{{arg.Name}}}", arg.ValueAsString)); + } + // Abort if the user pressed Cancel + else return; + } + script = script.ReplaceRange(contextDefinedArgs.Concat(userDefinedArgs)); DTE2 dte = (DTE2)await this.GetServiceAsync(typeof(DTE)); if (dte == null) diff --git a/SSMSObjectExplorerMenu.csproj b/SSMSObjectExplorerMenu.csproj index 4e95934..7a43270 100644 --- a/SSMSObjectExplorerMenu.csproj +++ b/SSMSObjectExplorerMenu.csproj @@ -65,10 +65,40 @@ AddMenuItem.cs - + + Form + + + AddUserDefinedParameter.cs + + + UserControl + + + ArgumentControl.cs + + + Form + + + EnterUserDefinedArguments.cs + + + + + + + + + + + + + + Component @@ -110,6 +140,7 @@ False + @@ -143,6 +174,15 @@ AddMenuItem.cs Designer + + AddUserDefinedParameter.cs + + + ArgumentControl.cs + + + EnterUserDefinedArguments.cs + ResXFileCodeGenerator Resources.Designer.cs diff --git a/UserDefinedArgumentTypeConverter.cs b/UserDefinedArgumentTypeConverter.cs new file mode 100644 index 0000000..ac9299b --- /dev/null +++ b/UserDefinedArgumentTypeConverter.cs @@ -0,0 +1,8 @@ +using SSMSObjectExplorerMenu.enums; + +namespace SSMSObjectExplorerMenu +{ + public class UserDefinedArgumentTypeConverter : EnumConverter + { + } +} diff --git a/Utils.cs b/Utils.cs new file mode 100644 index 0000000..bf1e9a2 --- /dev/null +++ b/Utils.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace SSMSObjectExplorerMenu +{ + public static class Utils + { + public static IEnumerable ParametersFromContext = new string[] + { + "OBJECT", + "SERVER", + "DATABASE", + "TABLE", + "VIEW", + "STORED_PROCEDURE", + "FUNCTION", + "SCHEMA", + "JOB", + "YYYY-MM-DD", + "HHmm:ss", + "YYYY-MM-DD HH:mm:ss" + }; + } +} diff --git a/enums/MenuItemContext.cs b/enums/MenuItemContext.cs new file mode 100644 index 0000000..8e940ec --- /dev/null +++ b/enums/MenuItemContext.cs @@ -0,0 +1,36 @@ +using System; +using System.ComponentModel; + +namespace SSMSObjectExplorerMenu.enums +{ + [Flags] + public enum MenuItemContext + { + [Description("Server")] + Server = 1, + [Description("Server/DatabasesFolder")] + DatabasesFolder = 2, + [Description("Server/Database")] + Database = 4, + [Description("Server/Database/Table")] + Table = 8, + [Description("Server/Database/Table/Column")] + Column = 16, + [Description("Server/Database/UserTablesFolder")] + UserTablesFolder = 32, + [Description("Server/Database/View")] + View = 64, + [Description("Server/Database/StoredProceduresFolder")] + StoredProceduresFolder = 128, + [Description("Server/Database/StoredProcedure")] + StoredProcedure = 256, + [Description("Server/JobServer")] + JobServer = 512, + [Description("Server/JobServer/JobsFolder")] + JobsFolder = 1024, + [Description("Server/JobServer/Job")] + Job = 2048, + [Description("All")] + All = Server | DatabasesFolder | Database | Table | Column | UserTablesFolder | View | StoredProceduresFolder | StoredProcedure | JobServer | JobsFolder | Job + } +} diff --git a/enums/UserDefinedArgumentType.cs b/enums/UserDefinedArgumentType.cs new file mode 100644 index 0000000..9d97bc9 --- /dev/null +++ b/enums/UserDefinedArgumentType.cs @@ -0,0 +1,21 @@ +using System.ComponentModel; + +namespace SSMSObjectExplorerMenu.enums +{ + public enum UserDefinedParameterType : byte + { + [Description("uniqueidentifier")] + UniqueIdentifier, + [Description("int")] + Int, + [Description("nvarchar")] + Nvarchar, + [Description("bit")] + Bit, + /// + /// This is not a SQL Server data type. Represents a list of options defined by the user. + /// + [Description("List of options")] + CustomList + } +} diff --git a/Extensions.cs b/extensions/Extensions.cs similarity index 62% rename from Extensions.cs rename to extensions/Extensions.cs index 9065efc..2a5e09a 100644 --- a/Extensions.cs +++ b/extensions/Extensions.cs @@ -1,10 +1,17 @@ using Microsoft.SqlServer.Management.UI.VSIntegration.ObjectExplorer; +using Newtonsoft.Json.Linq; +using SSMSObjectExplorerMenu.enums; using SSMSObjectExplorerMenu.objects; using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; using System.IO; +using System.Linq; +using System.Text.RegularExpressions; using System.Xml.Serialization; -namespace SSMSObjectExplorerMenu +namespace SSMSObjectExplorerMenu.extensions { public static class Extensions { @@ -28,7 +35,6 @@ public static string SerializeObject(this T toSerialize) } } - public static NodeInfo GetInfo(this INodeInformation node) { NodeInfo info = new NodeInfo @@ -77,8 +83,6 @@ public static NodeInfo GetInfo(this INodeInformation node) continue; } - - if (s.StartsWith("StoredProcedure[@Name='")) { string[] t = s.Replace("StoredProcedure[@Name='", "").Replace("' and @Schema='", "|").Replace("']", "").Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); @@ -115,11 +119,54 @@ public static NodeInfo GetInfo(this INodeInformation node) } } - return info; } + public static string ReplaceRange(this string original, IEnumerable<(string Phrase, string Value)> elements) + { + string result = original; + foreach ((string Phrase, string Value) in elements) + { + var replacementRegex = Regex.Escape(Phrase); + result = Regex.Replace(result, replacementRegex, Value, RegexOptions.IgnoreCase); + } + return result; + } + + public static string ToStringDescription(this T context) where T : Enum + { + var enumType = typeof(T); + var name = Enum.GetName(enumType, context); + if (name != null) + { + var field = enumType.GetField(name); + if (field != null) + { + var attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute; + if (attr != null) + { + return attr.Description; + } + } + } + throw new ArgumentException($"Unknown {nameof(T)} value.", nameof(context)); + } + + public static T FromDescription(this string enumDescription) where T : Enum + { + if (enumDescription == null) + { + throw new ArgumentNullException(nameof(enumDescription)); + } + var enumField = typeof(T).GetFields() + .SingleOrDefault(f => enumDescription.Equals( + (Attribute.GetCustomAttribute(f, typeof(DescriptionAttribute)) as DescriptionAttribute)?.Description, + StringComparison.OrdinalIgnoreCase)); - } + return enumField != null + ? (T)enumField.GetValue(null) + : throw new ArgumentException($"Unknown {nameof(T)} description.", nameof(enumDescription)); + } + } } diff --git a/extensions/UIExtensions.cs b/extensions/UIExtensions.cs new file mode 100644 index 0000000..0d58367 --- /dev/null +++ b/extensions/UIExtensions.cs @@ -0,0 +1,31 @@ +using SSMSObjectExplorerMenu.objects; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace SSMSObjectExplorerMenu.extensions +{ + internal static class UIExtensions + { + public static IEnumerable GetUserDefinedParams(this ListView listView) + { + foreach (ListViewItem item in listView.Items) + { + yield return item.Tag as UserDefinedParameter; + } + } + + public static UserDefinedArgument ToUserDefinedArgument(this ArgumentControl control) + => new UserDefinedArgument(control.Parameter.Name, control.Parameter.Type, control.ParameterValueString); + + public static void RemoveRange(this ListView.ListViewItemCollection litViewItems, IEnumerable itemsToRemove) + { + foreach (var item in itemsToRemove) + { + litViewItems.Remove(item); + } + } + + public static IEnumerable GetSelectedItems(this ListView listView) => listView.Items.Cast().Where(item => item.Selected); + } +} diff --git a/images/AddMenuItem.png b/images/AddMenuItem.png index 0279bf4..80f35ae 100644 Binary files a/images/AddMenuItem.png and b/images/AddMenuItem.png differ diff --git a/images/Options.png b/images/Options.png index 526ecff..32eab70 100644 Binary files a/images/Options.png and b/images/Options.png differ diff --git a/images/Options_parameter.png b/images/Options_parameter.png new file mode 100644 index 0000000..3e10ccb Binary files /dev/null and b/images/Options_parameter.png differ diff --git a/objects/ComboBoxItem.cs b/objects/ComboBoxItem.cs new file mode 100644 index 0000000..1ebd668 --- /dev/null +++ b/objects/ComboBoxItem.cs @@ -0,0 +1,8 @@ +namespace SSMSObjectExplorerMenu.objects +{ + public class ComboBoxItem + { + public string Displayed { get; set; } + public TValue Value { get; set; } + } +} diff --git a/objects/MenuItem.cs b/objects/MenuItem.cs index 8329793..ed6a074 100644 --- a/objects/MenuItem.cs +++ b/objects/MenuItem.cs @@ -1,7 +1,12 @@ -using System; +using SSMSObjectExplorerMenu.enums; +using SSMSObjectExplorerMenu.extensions; +using System; +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.Drawing.Design; +using System.Linq; +using System.Xml.Serialization; namespace SSMSObjectExplorerMenu.objects { @@ -48,27 +53,58 @@ public string Script [Category("Menu item")] [DisplayName("Context")] [Description("Tree node level where to display menu item.")] - [DefaultValue("All")] - public string Context { get; set; } = "All"; + [DefaultValue(MenuItemContext.All)] + [TypeConverter(typeof(MenuItemContextConverter))] + [XmlIgnore] + public MenuItemContext Context { get; set; } = MenuItemContext.All; + [Browsable(false)] + [XmlElement("Context")] + public string ContextDescription + { + get => Context.ToStringDescription(); + set => Context = value.FromDescription(); + } + + [Category("Menu item")] + [DisplayName("User-defined parameters")] + [Description("List of user-deifned parameters can be used in the T-SQL script.")] + [Editor(typeof(CollectionEditor), typeof(UITypeEditor))] + public BindingList UserDefinedParameters { get; private set; } = new BindingList(); public MenuItem() { } - public MenuItem(bool enabled, string context, string name, string script, bool execute, bool confirm) + public MenuItem(bool enabled, MenuItemContext context, string name, string script, bool execute, bool confirm, IEnumerable userDefinedParams = null) { - Enabled = enabled; + Enabled = enabled; Context = context; Name = name; Script = script; Execute = execute; Confirm = confirm; + foreach (var param in userDefinedParams ?? Enumerable.Empty()) + { + UserDefinedParameters.Add(param); + } + if (Confirm) { Execute = true; } } + + public bool TryValidate(out IEnumerable validationErrors) + { + validationErrors = + UserDefinedParameters.Select( + // reserved names: names coming from context + names of other user-defined parameters of the MenuItem + p => p.TryValidate(out IEnumerable paramErrors, Utils.ParametersFromContext.Concat(UserDefinedParameters.Where(pa => pa != p).Select(pa => pa.Name))) + ? null : new MenuItemErrorModel { MenuItemName = Name, ErrorMessages = paramErrors }) + .Where(e => e != null); + return !validationErrors.Any(); + } } } diff --git a/objects/MenuItemErrorModel.cs b/objects/MenuItemErrorModel.cs new file mode 100644 index 0000000..cb6ffc0 --- /dev/null +++ b/objects/MenuItemErrorModel.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace SSMSObjectExplorerMenu.objects +{ + public class MenuItemErrorModel + { + public string MenuItemName { get; set; } + + public IEnumerable ErrorMessages { get; set; } + } +} diff --git a/objects/StringListItem.cs b/objects/StringListItem.cs new file mode 100644 index 0000000..b4e99db --- /dev/null +++ b/objects/StringListItem.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; + +namespace SSMSObjectExplorerMenu.objects +{ + /// + /// This class is a wrapper for using it with in property grid. + /// calls the default constructor when adding a new item to the collection. + /// does not have a default parameterless constructor. + /// + public class StringListItem + { + public string Value { get; set; } = string.Empty; + + public StringListItem() { } + public StringListItem(string value) { Value = value; } + + public override string ToString() => Value; + + public static implicit operator string(StringListItem item) => item?.Value; + + public static implicit operator StringListItem(string value) => new StringListItem(value); + } + + public class StringListItemComparer : IEqualityComparer + { + private StringComparison? _comparison; + + public StringListItemComparer() { } + + public StringListItemComparer(StringComparison comparison) => _comparison = comparison; + + public bool Equals(StringListItem x, StringListItem y) + { + if (ReferenceEquals(x, y)) return true; + if (x is null || y is null) return false; + return _comparison.HasValue ? string.Equals(x.Value, y.Value, _comparison.Value) : string.Equals(x.Value, y.Value); + } + + public int GetHashCode(StringListItem obj) + { + if (obj?.Value == null) return 0; + + switch (_comparison) + { + case StringComparison.OrdinalIgnoreCase: + return obj.Value.ToLowerInvariant().GetHashCode(); + case StringComparison.CurrentCultureIgnoreCase: + return obj.Value.ToLower(System.Globalization.CultureInfo.CurrentCulture).GetHashCode(); + case StringComparison.InvariantCultureIgnoreCase: + return obj.Value.ToLower(System.Globalization.CultureInfo.InvariantCulture).GetHashCode(); + case StringComparison.Ordinal: + case StringComparison.CurrentCulture: + case StringComparison.InvariantCulture: + default: + return obj.Value.GetHashCode(); + } + } + } +} diff --git a/objects/UserDefinedArgument.cs b/objects/UserDefinedArgument.cs new file mode 100644 index 0000000..c2c9745 --- /dev/null +++ b/objects/UserDefinedArgument.cs @@ -0,0 +1,35 @@ +using Microsoft.Internal.VisualStudio.Shell; +using SSMSObjectExplorerMenu.enums; +using System; + +namespace SSMSObjectExplorerMenu.objects +{ + public class UserDefinedArgument : UserDefinedParameter + { + private string _valueStr; + + public string ValueAsString { + get => _valueStr; + private set + { + ValidateValue(value); + _valueStr = value; + } + } + + public UserDefinedArgument(string name, UserDefinedParameterType type, string value) + { + Name = name; + Type = type; + ValueAsString = value; + } + + private static void ValidateValue(string value) + { + if (value is null) + { + throw new ArgumentNullException(nameof(value), "Value cannot be null."); + } + } + } +} diff --git a/objects/UserDefinedParameter.cs b/objects/UserDefinedParameter.cs new file mode 100644 index 0000000..12155d6 --- /dev/null +++ b/objects/UserDefinedParameter.cs @@ -0,0 +1,82 @@ +using SSMSObjectExplorerMenu.enums; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing.Design; +using System.Linq; + +namespace SSMSObjectExplorerMenu.objects +{ + [TypeConverter(typeof(ExpandableObjectConverter))] + public class UserDefinedParameter + { + public const short NAME_MAX_LENGTH = 64; + + [DisplayName("Name")] + [Description("Name of the user-defined parameter. Cannot be empty or contain only whitespace characters.")] + public string Name { get; set; } + + [DisplayName("Data type")] + [Description("Data type of the user-defined parameter.")] + [TypeConverter(typeof(UserDefinedArgumentTypeConverter))] + public UserDefinedParameterType Type { get; set; } + + /// + /// Populate if is . Otherwise it's ignored. + /// + [DisplayName("List of options")] + [Description("List of options. If the parameter's data type is not \"List of options\", it is ignored.")] + [Editor(typeof(CollectionEditor), typeof(UITypeEditor))] + public BindingList ValueSetOfCustomList { get; set; } = new BindingList(); + + public bool TryValidate(out IEnumerable validationErrors, IEnumerable reservedNames = null) + { + var errors = new List(); + if(!TryValidateName(Name, out IEnumerable nameErrors, reservedNames)) + { + errors.AddRange(nameErrors); + } + + if (Type == UserDefinedParameterType.CustomList) + { + if (ValueSetOfCustomList is null || ValueSetOfCustomList.Count == 0) + { + errors.Add($"Parameter '{Name}': list of options cannot be empty."); + } + if (ValueSetOfCustomList.Any(value => string.IsNullOrWhiteSpace(value))) + { + errors.Add($"Parameter '{Name}': list of options cannot contain empty or whitespace-only elements."); + } + if(ValueSetOfCustomList.Distinct(new StringListItemComparer(StringComparison.OrdinalIgnoreCase)).Count() != ValueSetOfCustomList.Count) + { + errors.Add($"Parameter '{Name}': list of options cannot have duplicate elements."); + } + } + + validationErrors = errors; + return !validationErrors.Any(); + } + + public static bool TryValidateName(string parameterName, out IEnumerable validationErrors, IEnumerable reservedNames = null) + { + var errors = new List(); + + if (string.IsNullOrWhiteSpace(parameterName)) + { + errors.Add("User-defined parameter name cannot be null or whitespace."); + } + if (parameterName?.Length > NAME_MAX_LENGTH) + { + errors.Add($"User-defined parameter name cannot be longer than {NAME_MAX_LENGTH} characters."); + } + if (reservedNames?.Any(reservedName => StringComparer.OrdinalIgnoreCase.Equals(reservedName, parameterName)) ?? false) + { + errors.Add($"Parameter name '{parameterName}' is reserved or duplicated."); + } + + validationErrors = errors; + return !validationErrors.Any(); + } + } +}