在设计时访问窗体自定义属性

本文关键字:窗体 自定义属性 访问 | 更新日期: 2023-09-27 18:26:44

我发现了ControlDesigner的一个示例,它向我展示了如何使用IEventBindingService添加控件和创建事件处理程序,然后使用CodeTypeDeclaration在该事件处理程序中添加一些代码。但是,当我尝试访问基窗体的自定义属性时,CodeTypeDeclaration返回了一个空集合。以下示例显示CodeTypeDeclaration不返回基本表单的任何自定义属性:

using System;
using System.CodeDom;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace WindowsFormsApplication1
{
    [MyCustom("new sample text")]
    public class MyForm : MyBaseForm
    {
        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // MyForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.ClientSize = new System.Drawing.Size(617, 450);
            this.ResumeLayout(false);
        }
        #endregion
        public MyForm()
        {
            InitializeComponent();
        }
    }
    [MyCustom("sample text")]
    [Designer(typeof(MyBaseFormDesigner), typeof(IRootDesigner))]
    public partial class MyBaseForm : Form
    {
        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // MyBaseForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(391, 337);
            this.ResumeLayout(false);
        }
        #endregion
        public MyBaseForm()
        {
            InitializeComponent();
        }
    }
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public class MyCustomAttribute : Attribute
    {
        public string Text { get; set; }
        public MyCustomAttribute(string text)
        {
            this.Text = text;
        }
    }
    public class MyBaseFormDesigner : DocumentDesigner
    {
        public override void Initialize(IComponent component)
        {
            base.Initialize(component);
            Verbs.Add(new DesignerVerb("Show CodeTypeDeclaration", OnShowCodeTypeDeclaration));
        }
        private static string GetCode(CodeTypeDeclaration codeType)
        {
            var code = new System.Text.StringBuilder();
            using (var provider = new Microsoft.CSharp.CSharpCodeProvider()) {
                using (var writer = new System.IO.StringWriter(code)) {
                    provider.GenerateCodeFromType(codeType, writer, new System.CodeDom.Compiler.CodeGeneratorOptions());
                }
            }
            return code.ToString();
        }
        protected virtual void OnShowCodeTypeDeclaration(object sender, EventArgs args)
        {
            var codeType = GetService(typeof(CodeTypeDeclaration)) as CodeTypeDeclaration;
            if (MessageBox.Show("Add MyCustomAttribute?", "", MessageBoxButtons.YesNo) == DialogResult.Yes) {
                codeType.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(MyCustomAttribute)), new CodeAttributeArgument(new CodePrimitiveExpression("sample text from designer"))));
            }
            MessageBox.Show(GetCode(codeType));
        }
    }
}

我尝试为我的表单使用自定义CodeDomSerializer,但使用这种方法,我只能访问InitializeComponent方法中的代码。有没有其他方法可以访问表单的自定义属性?

我想要这样做的原因是,我可以在设计器中创建一个操作,在窗体上添加/更改我的自定义属性的参数。

在设计时访问窗体自定义属性

我终于找到了我想要的东西,使用EnvDTE可以访问属性(和导入)。以下是我可以添加/更改属性值的完整示例:

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace WindowsFormsApplication1
{
    [MyCustom("new sample text")]
    public class MyForm : MyBaseForm
    {
        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // MyForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.ClientSize = new System.Drawing.Size(617, 450);
            this.ResumeLayout(false);
        }
        #endregion
        public MyForm()
        {
            InitializeComponent();
        }
    }
    [MyCustom("sample text")]
    [Designer(typeof(MyBaseFormDesigner), typeof(IRootDesigner))]
    public partial class MyBaseForm : Form
    {
        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // MyBaseForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(391, 337);
            this.ResumeLayout(false);
        }
        #endregion
        public MyBaseForm()
        {
            InitializeComponent();
        }
    }
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public class MyCustomAttribute : Attribute
    {
        public string Text { get; set; }
        public MyCustomAttribute(string text = "")
        {
            this.Text = text;
        }
    }
    public class MyBaseFormDesigner : DocumentDesigner
    {
        public override void Initialize(IComponent component)
        {
            base.Initialize(component);
            Verbs.Add(new DesignerVerb("Edit MyCustomAttribute text", OnEditText));            
        }
        protected virtual void OnEditText(object sender, EventArgs args)
        {
            EnvDTE._DTE dte = GetService(typeof(EnvDTE._DTE)) as EnvDTE._DTE;
            EnvDTE80.CodeClass2 codeClass = GetCodeClass(dte.ActiveDocument.ProjectItem.FileCodeModel.CodeElements, "MyForm");
            EnvDTE80.CodeAttribute2 codeAttribute = GetCodeAttribute(codeClass, "MyCustom");
            if (codeAttribute != null) {
                string newValue = Microsoft.VisualBasic.Interaction.InputBox("Current Text", "Edit MyCustomAttribute text", RemoveQuote(codeAttribute.Value), -1, -1);
                codeAttribute.Value = (!string.IsNullOrWhiteSpace(newValue) ? "'"" + newValue + "'"" : "");
            }
        }
        private static string RemoveQuote(string str)
        {
            return !string.IsNullOrEmpty(str) && str[0] == '"' && str[str.Length - 1] == '"' ? str.Substring(1, str.Length - 2) : str;
        }
        private static EnvDTE80.CodeClass2 GetCodeClass(EnvDTE.CodeElements codeElements, string className)
        {
            if (codeElements != null) {
                foreach (EnvDTE.CodeElement item in codeElements) {
                    if (item.Kind == EnvDTE.vsCMElement.vsCMElementClass) {
                        EnvDTE80.CodeClass2 codeClass = item as EnvDTE80.CodeClass2;
                        if (codeClass != null && codeClass.Name == className) return codeClass;
                    } else if (item.Kind == EnvDTE.vsCMElement.vsCMElementNamespace) {
                        EnvDTE80.CodeClass2 codeClass = GetCodeClass(((EnvDTE.CodeNamespace)item).Members, className);
                        if (codeClass != null && codeClass.Name == className) return codeClass;
                    }
                }
            }
            return null;
        }
        private static EnvDTE80.CodeAttribute2 GetCodeAttribute(EnvDTE80.CodeClass2 codeClass, string attributeName)
        {
            if (codeClass != null) {
                foreach (EnvDTE80.CodeAttribute2 attr in codeClass.Attributes) {
                    if (attr.Name == attributeName) return attr;
                }
                return codeClass.AddAttribute(attributeName, "") as EnvDTE80.CodeAttribute2;
            }
            return null;
        }
    }
}