none
如何使用struct作为控件的属性 RRS feed

  • 问题

  • 因为需要扩展TreeView功能,所以做了一个新控件。控件中有一个Struct属性TreeNodeMember,并且写了相应的TypeConverter类,可以在设计期在属性编辑器中编辑此数据。但是编辑完后的属性无法自动生成代码,窗体关闭以后就丢失了。

    大家帮忙看看,多谢!!!

        public partial class TreeView2 : TreeView
        {
            private string idMember;
            private string codeMember;
            private string nameMember;
            private string levelMember;
            private string parentMember;
            private string enabledMember;
            private string leafMember;
            private string typeMember;

            public TreeNodeMember NodeMember
            {
                get
                {
                    return new TreeNodeMember(idMember, codeMember, nameMember, levelMember, leafMember,
                        enabledMember, typeMember, parentMember);
                }
                set
                {
                    idMember = value.IDMember;
                    codeMember = value.CodeMember;
                    nameMember = value.NameMember;
                    levelMember = value.LevelMember;
                    leafMember = value.LeafMember;
                    enabledMember = value.EnabledMember;
                    typeMember = value.TypeMember;
                    parentMember = value.ParentMember;
                    ClearTree();
                }
            }

            public TreeView()
            {
                InitializeComponent();

                idMember = "ID";
                codeMember = "Code";
                nameMember = "Name";
                parentMember = "ParentID";

            }

       }

           [TypeConverter(typeof(TreeNodeMemberConverter))]
           public struct TreeNodeMember

          {

                 属性、方法省略

          }

        public class TreeNodeMemberConverter : TypeConverter
        {
            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                if (destinationType == typeof(string))
                {
                    return true;
                }
                return base.CanConvertTo(context, destinationType);
            }

            public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,
                object value, Type destinationType)
            {
                if (destinationType == null)
                {
                    throw new ArgumentNullException("destinationType");
                }
                if (value is TreeNodeMember)
                {
                    if (destinationType == typeof(string))
                    {
                        TreeNodeMember member = (TreeNodeMember)value;
                        return member.ToString();
                    }
                }
                return base.ConvertTo(context, culture, value, destinationType);
            }

            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
            {
                if (sourceType == typeof(string))
                {
                    return true;
                }
                return base.CanConvertFrom(context, sourceType);
            }

            public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                string strValue = value as string;
                if (strValue != null)
                {
                    string text = strValue.Trim();
                    if (text.Length == 0)
                    {
                        return null;
                    }
                    else
                    {
                        TreeNodeMember member = new TreeNodeMember();
                        member.Parse(text);

                        return member;
                    }
                }

                return base.ConvertFrom(context, culture, value);
            }

            public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
            {
                if (propertyValues == null)
                {
                    throw new ArgumentNullException("propertyValues");
                }
                object id = propertyValues["IDMember"];
                object code = propertyValues["CodeMember"];
                object name = propertyValues["NameMember"];
                object level = propertyValues["LevelMember"];
                object leaf = propertyValues["LeafMember"];
                object type = propertyValues["TypeMember"];
                object enabled = propertyValues["EnabledMember"];
                object parent = propertyValues["ParentMember"];

                TreeNodeMember member = new TreeNodeMember((string)id, (string)code, (string)name, (string)level,
                    (string)leaf, (string)enabled, (string)type, (string)parent);
                return member;
            }

            public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
            {
                return true;
            }

            public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
            {
                PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(TreeNodeMember), attributes);
                return props.Sort(new string[] { "IDMember", "CodeMember", "NameMember", "ParentMember", "LevelMember", "LeafMember", "EnabledMember", "TypeMember" });
            }

            public override bool GetPropertiesSupported(ITypeDescriptorContext context)
            {
                return true;
            }
        }

    2012年11月20日 8:30

答案

  • 那你这么说是不能在UserControl上使用struct属性了,这个好像说不过去。

    我做了一下修改,可以存储了,但是是序列号以后存储在资源文件中,而不是生成代码。应该还有解决方案

    [TypeConverter(typeof(MyConvertor))]
    [Serializable]
    public struct DPoint

    会自动生成代码:

    this.userControl11.Point = ((grp.gl.control.DPoint)(resources.GetObject("userControl11.Point")));

    而不是

    this.userControl11.Point = new DPoint(1, 4, 3);

    2012年11月23日 2:58

全部回复

  • 大侠们哪去了,赶紧帮忙啊

    2012年11月21日 1:48
  • 此类属性建议你还是使用在类上比较好。因为是专门为类设计的。建议你把struct改成class。

    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    2012年11月21日 3:08
    版主
  • class和struct应该没啥区别,.net framework中control用的size和location都是struct

    2012年11月21日 3:31
  • class和struct应该没啥区别,.net framework中control用的size和location都是struct

    额,好的,我看看哦……

    你是WinForm还是WebForm的?麻烦你可以把代码发送到maledong_work@foxmail.com,附上你的问题Url。谢谢!


    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    2012年11月21日 7:57
    版主
  • 你好,代码已经收到。不过引用第三方的控件无法看清。我自己做一个例子你可以模拟参考,我也用struct,和你一样的结果。用class同步了:

    namespace CSharp
    {
        /// <summary>
        /// 三维坐标的Struct结构
        /// </summary>
        [TypeConverter(typeof(MyConvertor))]
        public class DPoint
        {
            private double _x;
            private double _y;
            private double _z;
            [NotifyParentProperty(true)]
            public double Z
            {
                get { return _z; }
                set { _z = value; }
            }
            [NotifyParentProperty(true)]
            public double Y
            {
                get { return _y; }
                set { _y = value; }
            }
            [NotifyParentProperty(true)]
            public double X
            {
                get { return _x; }
                set { _x = value; }
            }
     
            public DPoint(double x,double y,double z)
            {
                _x = x;
                _y = y;
                _z = z;
            }
            public static explicit operator DPoint(string s)
            {
                string[] values = s.Split(new string[]{","},StringSplitOptions.RemoveEmptyEntries);
                if (values.Length == 3)
                {
                    double d = Convert.ToDouble(values[0]);
                    double d2 = Convert.ToDouble(values[1]);
                    double d3= Convert.ToDouble(values[2]);
                    return new DPoint(d, d2, d3);
                }
                throw new Exception("输入的维数太多或者太少!");
            }
        }
        
        /// <summary>
        /// 自定义从String转化成DPoint类型 
        /// </summary>
        public class MyConvertor:ExpandableObjectConverter
        {
            /// <summary>
            ///  判断只能string类型作为转换对象,即该标签只加载String上有效
            /// </summary>
            /// <param name="context"></param>
            /// <param name="sourceType"></param>
            /// <returns></returns>
            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
            {
                return typeof(string) == sourceType.GetType();
            }
     
            public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                return (DPoint)value.ToString();
            }
     
            public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
            {
                if (value is DPoint)
                {
                    DPoint dp = (DPoint)value;
                    return string.Format("{0},{1},{2}", dp.X, dp.Y, dp.Z);
                }
                throw new Exception("无法转化成标准的String格式!");
            }
            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                return destinationType.GetType() == typeof(string);
            }
     
        }
        /// <summary>
        /// 三位数组自定义类
        /// </summary>
        [ParseChildren(true),PersistChildren(false)]
        public class MyDPoint:WebControl
        {
            DPoint dp = null;
     
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
            [PersistenceMode(PersistenceMode.InnerProperty)]
            [NotifyParentProperty(true)]
            public DPoint DPoint
            {
                get
                {
                    if (dp == null)
                    {
                        dp = new DPoint(0, 0, 0);
                    }
                    return dp;
                }
                set
                {
                    dp = value;
                }
            }
     
            protected override void Render(HtmlTextWriter writer)
            {
                writer.Write(string.Format("[{0},{1},{2}]", DPoint.X, DPoint.Y, DPoint.Z));
            }
        }
    }

    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    2012年11月21日 11:49
    版主
  • 非常感谢!我改成class试试。

    但是我觉得可能还是哪里不对,因为Size和Point确实是Struct啊。在界面设计的时候拖一个控件到界面上,vs会自动生成这样的代码:

    this.rwPanel1.Location = new System.Drawing.Point(12, 12);

    this.rwPanel1.Size = new System.Drawing.Size(468, 469);

    应该是想办法让vs自动生成这个代码。

    2012年11月22日 1:39
  • 你这是WinForm吧?在WebForm中,代码主要是在aspx生成,譬如:

    <cc1:MyDPoint ID="MyDPoint1" runat="server">
                <DPoint X="1" Y="2" Z="3" />
    </cc1:MyDPoint>

    注意我重新写的代码,粗体部分,改成Struct完全可以!

    namespace CSharp
    {
        namespace CSharp
        {
            /// <summary>
            /// 三维坐标的Struct结构
            /// </summary>
            [TypeConverter(typeof(MyConvertor))]
            public struct DPoint
            {
                private double _x;
                private double _y;
                private double _z;
                [NotifyParentProperty(true)]
                public double Z
                {
                    get { return _z; }
                    set { _z = value; }
                }
                [NotifyParentProperty(true)]
                public double Y
                {
                    get { return _y; }
                    set { _y = value; }
                }
                [NotifyParentProperty(true)]
                public double X
                {
                    get { return _x; }
                    set { _x = value; }
                }
     
                public DPoint(double x, double y, double z)
                {
                    _x = x;
                    _y = y;
                    _z = z;
                }
                public static explicit operator DPoint(string s)
                {
                    string[] values = s.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
                    if (values.Length == 3)
                    {
                        double d = Convert.ToDouble(values[0]);
                        double d2 = Convert.ToDouble(values[1]);
                        double d3 = Convert.ToDouble(values[2]);
                        return new DPoint(d, d2, d3);
                    }
                    throw new Exception("输入的维数太多或者太少!");
                }
            }
     
            /// <summary>
            /// 自定义从String转化成DPoint类型 
            /// </summary>
            public class MyConvertor : ExpandableObjectConverter
            {
                /// <summary>
                ///  判断只能string类型作为转换对象,即该标签只加载String上有效
                /// </summary>
                /// <param name="context"></param>
                /// <param name="sourceType"></param>
                /// <returns></returns>
                public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
                {
                    return typeof(string) == sourceType.GetType();
                }
     
                public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
                {
                    return (DPoint)value.ToString();
                }
     
                public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
                {
                    if (value is DPoint)
    
                    {
                        DPoint dp = (DPoint)value;
                        return string.Format("{0},{1},{2}", dp.X, dp.Y, dp.Z);
                    }
                    throw new Exception("无法转化成标准的String格式!");
                }
                public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
                {
                    return destinationType.GetType() == typeof(string);
                }
     
                public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
                {
                    return new DPoint((double)propertyValues["X"], (double)propertyValues["Y"],(double)propertyValues["Z"]);
                }
                public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
                {
                    return true;
                }
            }
            /// <summary>
            /// 三位数组自定义类
            /// </summary>
            [ParseChildren(true), PersistChildren(false)]
            public class MyDPoint : WebControl
            {
                DPoint dp = new DPoint();
     
                [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
                [PersistenceMode(PersistenceMode.InnerProperty)]
                [NotifyParentProperty(true)]
                public DPoint DPoint
                {
                    get
                    {
                        return dp;
                    }
                    set
                    {
                        dp = value;
                    }
                }
     
                protected override void Render(HtmlTextWriter writer)
                {
                    writer.Write(string.Format("[{0},{1},{2}]", DPoint.X, DPoint.Y, DPoint.Z));
                }
            }
        }
    }

    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    2012年11月22日 2:09
    版主
  • 我的还是不行,另外没明白ExpandableObjectConverter对TypeConverter扩展了什么,GetStandardValuesSupported方法起什么作用

    2012年11月22日 4:01
  • 我的还是不行,另外没明白ExpandableObjectConverter对TypeConverter扩展了什么,GetStandardValuesSupported方法起什么作用

    是可以得。你只要设置DPoint就可以了,我把项目回发给你(你看看,我用VS2012的)。

    ExpandableObjectConverter对TypeConverter扩展在于——把每个公开的属性允许在属性面板中展示出来。GetStandardValuesSupported允许你直接在属性面板中编辑数据。


    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    2012年11月22日 4:41
    版主
  • 我把你的代码用到一个UserControl上,在winForm程序中是不行的。

    如果把DPoint改成class,和struct一样无法存储。难道你用webform可以,winForm就不成了?

    2012年11月22日 6:57
  • 对不起,我首先是WebForm。

    其次,不要放到UserControl,而是重新创建一个Class黏贴我的代码,然后会自动在工具条上生成控件的。


    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    2012年11月22日 7:22
    版主
  • 为什么不能用userControl,不是一个道理吗

    2012年11月23日 1:28
  • 为什么不能用userControl,不是一个道理吗


    UserControl实现机制和自定义控件(可发布控件)是不同的。

    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    2012年11月23日 1:36
    版主
  • 那你这么说是不能在UserControl上使用struct属性了,这个好像说不过去。

    我做了一下修改,可以存储了,但是是序列号以后存储在资源文件中,而不是生成代码。应该还有解决方案

    [TypeConverter(typeof(MyConvertor))]
    [Serializable]
    public struct DPoint

    会自动生成代码:

    this.userControl11.Point = ((grp.gl.control.DPoint)(resources.GetObject("userControl11.Point")));

    而不是

    this.userControl11.Point = new DPoint(1, 4, 3);

    2012年11月23日 2:58
  • 老邸

    并非这个意思,我的意思是你给我的示例代码是aspx的,并不是UserControl。我以为你是WebForm的呢!呵呵。


    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    2012年11月24日 0:30
    版主
  • 非常感谢 编程志愿者 的帮助,我用你的WebForm确实可以存储,但是用你的代码生成一个UserControl,同样无法解决问题,不知道是什么原因?

    我加上[Serializable],确实在设计期可以存储了,但是后来发现根本无法编译,我正在检查这个问题,可能是资源文件的字符串无法解析成对象。

    如果有时间,帮我用WinForm看一下怎么解决,谢谢!!!

    2012年11月26日 1:53
  • 修改TreeNodeMemberConverter以后可以使用了,但是在我同事的机器上却还是无法使用。

    各位专家有时间的时候帮忙看看

        public class TreeNodeMemberConverter : TypeConverter
        {
            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                if (destinationType == typeof(InstanceDescriptor))
                {
                    return true;
                }
                return base.CanConvertTo(context, destinationType);
            }
    
            public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture,
                object value, Type destinationType)
            {
                if (destinationType == null)
                {
                    throw new ArgumentNullException("destinationType");
                }
                if (value is TreeNodeMember)
                {
                    if (destinationType == typeof(string))
                    {
                        TreeNodeMember member = (TreeNodeMember)value;
                        return member.ToString();
                    }
                    else if (destinationType == typeof(InstanceDescriptor))
                    {
                        TreeNodeMember member = (TreeNodeMember)value;
                        Type[] argTypes = { typeof(String), typeof(String), typeof(String), typeof(String), 
                                            typeof(String), typeof(String), typeof(String), typeof(String) };
                        object[] properties = { member.IDMember, member.CodeMember, member.NameMember, 
                                                member.LevelMember, member.LeafMember, member.EnabledMember, 
                                                member.TypeMember, member.ParentMember };
                        ConstructorInfo ci = typeof(TreeNodeMember).GetConstructor(argTypes);
                        return new InstanceDescriptor(ci, properties);
                    }
                }
                return base.ConvertTo(context, culture, value, destinationType);
            }
    
            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
            {
                if (sourceType == typeof(string))
                {
                    return true;
                }
                return base.CanConvertFrom(context, sourceType);
            }
    
            public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                string strValue = value as string;
                if (strValue != null)
                {
                    string text = strValue.Trim();
                    if (text.Length == 0)
                    {
                        return null;
                    }
                    else
                    {
                        TreeNodeMember member = new TreeNodeMember();
                        member.Parse(text);
                        return member;
                    }
                }
    
                return base.ConvertFrom(context, culture, value);
            }
    
            public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
            {
                if (propertyValues == null)
                {
                    throw new ArgumentNullException("propertyValues");
                }
                object id = propertyValues["IDMember"];
                object code = propertyValues["CodeMember"];
                object name = propertyValues["NameMember"];
                object level = propertyValues["LevelMember"];
                object leaf = propertyValues["LeafMember"];
                object type = propertyValues["TypeMember"];
                object enabled = propertyValues["EnabledMember"];
                object parent = propertyValues["ParentMember"];
    
                TreeNodeMember member = new TreeNodeMember((id as string), (code as string), (name as string),
                    (level as string), (leaf as string), (enabled as string), (type as string), (parent as string));
                return member;
            }
    
            public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
            {
                return true;
            }
    
            public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
            {
                PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(TreeNodeMember), attributes);
                return props.Sort(new string[] { "IDMember", "CodeMember", "NameMember", "ParentMember", "LevelMember", "LeafMember", "EnabledMember", "TypeMember" });
            }
    
            public override bool GetPropertiesSupported(ITypeDescriptorContext context)
            {
                return true;
            }
        }

    2012年12月24日 6:49
  • 可以工作了,但好像还是不稳定。有时候能自动生成代码,有时候又不自动生成代码,添加控件以后保存会报无法将TreeNodeMember 转化为System.ComponentModel.Design.Serialization.InstanceDescriptor

    头痛中……

    2013年1月16日 3:33
  • 你的问题应该这样解决:把你的 TreeNodeMember 和 TreeNodeMemberConverter 放到一个单独的程序集中,假设为 TreeViewHelper.dll,然后在你要使用的项目中通过“浏览”到 TreeViewHelper.dll 的方式来添加引用,注意不要直接引用项目(部署时,可以将TreeViewHelper.dll部署到GAC,通过GAC来引用)。

    2013年1月16日 7:57
  • 没有明白什么意思,必须这样做吗?.net源代码中也是这样做的?

    2013年11月18日 3:20