我有一个自定义的控件,是扩展标准TextBox的,功能就是增加一个水印效果,本来在VS2008的写了一个,也没有什么大问题,但我把它弄到VS2010上的时候就会出现一些莫名其妙的报错:每次设置属性WaterMark后按F5,就会提示生成代码错误,未将对象引用到对象实例,或是类型为WaterMarkStyle的对象无法转换为WaterMarkStyle,.....还有就是如果我在窗体上创建这个定义的TextBox的一个实例,并在属性窗口的WaterMark属性上右击选"重置",再按F5,一定会出现下图的错误消息:同时,属性窗口中WaterMark前面的+号也消失,不会再出现里面的Text和Color属性下观是节选的部分代码:
        //
        //自定义的TextBox中WaterMark属性的定义
        //
        static object WaterMarkChangedEvent = new object();
        /// <summary>
        /// 当WaterMark属性更改时触发.
        /// </summary>
        public event EventHandler WaterMarkChanged
        {
            add { Events.AddHandler(WaterMarkChangedEvent, value); }
            remove { Events.RemoveHandler(WaterMarkChangedEvent, value); }
        }
        WaterMarkStyle waterMark = new WaterMarkStyle("",SystemColors.GrayText);
        /// <summary>
        /// 获取或设置控件的水印效果.
        /// </summary>
        [DefaultValue(typeof(WaterMarkStyle),"")]
        [Browsable(true)]
        public WaterMarkStyle WaterMark
        {
            get { return waterMark; }
            set 
            {
                if (waterMark==value) return ;
                waterMark = value;
                OnWaterMarkChanged(EventArgs.Empty);
            }
        }
       //... ...
    //
    //WaterMarkStyle类型定义
    //
    /// <summary>
    /// 可编辑控件的水印样式
    /// </summary>
    [TypeConverter(typeof(WaterMarkStyleTypeConverter))]
    [ComVisible(true)]
    [Serializable]
    public struct WaterMarkStyle
    {
        string text;
        Color color;
        /// <summary>
        /// 获取或设置水印文本.
        /// </summary>
        [DefaultValue("")]
        public string Text
        {
          
            get { return text; }
            set 
            {
                if (text == value) return;
                text = value;
            }
        }
        /// <summary>
        /// 获取或设置水印颜色.
        /// </summary>
        [DefaultValue(typeof(Color),"GrayText")]
        public Color Color
        {
            get { return color; }
            set 
            {
                if (color==value) return ;
                color = value;
            }
        }
        /// <summary>
        /// 重写父类的判等方法.
        /// </summary>
        /// <param name="obj">与当前实例比较的目标对象</param>
        /// <returns>如果两个对象相等则返回true,否则返回false.</returns>
        public override bool Equals(object obj)
        {
            if (obj == null || !(obj is WaterMarkStyle)) return false;
            WaterMarkStyle wms = (WaterMarkStyle)obj;
            return this.text == wms.text && this.color == wms.color;
        }
        /// <summary>
        /// 获取类型当前实例的哈希码.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.text.GetHashCode() ^ this.color.GetHashCode();
        }
        /// <summary>
        /// 获取类型当前实例的文本值.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.text;
        }        /// <summary>
        /// 判断给定两个参数是否相等
        /// </summary>
        /// <param name="wm1"></param>
        /// <param name="wm2"></param>
        /// <returns></returns>
        public static bool operator  ==(WaterMarkStyle wm1,WaterMarkStyle wm2)
        {
            if (wm1==null ^wm2==null)
            {
                return false;
            }
            return wm1.text == wm2.text && wm1.color.ToArgb() == wm2.color.ToArgb();
        }
        /// <summary>
        /// 判断给定两个参数是否不等.
        /// </summary>
        /// <param name="wm1"></param>
        /// <param name="wm2"></param>
        /// <returns></returns>
        public static bool operator !=(WaterMarkStyle wm1, WaterMarkStyle wm2)
        {
            if (wm1 == null ^ wm2 == null)
            {
                return true;
            }
            return wm1.text != wm2.text || wm1.color.ToArgb() != wm2.color.ToArgb();
        }
        /// <summary>
        /// 根据给定的文本和文本颜色,构造当前类型实例.
        /// </summary>
        /// <param name="text"></param>
        /// <param name="color"></param>
        public WaterMarkStyle(string text,Color color):this()
        {
            this.text = text;
            this.color = color;
        }
        /// <summary>
        /// 根据给定的文本和默认颜色,构造当前类型实例.
        /// </summary>
        /// <param name="text"></param>
        public WaterMarkStyle(string text) : this() 
        {
            this.text = text;
            this.color =SystemColors.GrayText;
        }
    }
    //类型转换器
    class WaterMarkStyleTypeConverter:TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType==typeof(string) || base.CanConvertFrom(context, sourceType);
        }
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return destinationType ==typeof( InstanceDescriptor) || base.CanConvertTo(context, destinationType);
        }
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value != null)// return new WaterMarkStyle("",SystemColors.GrayText);// throw new ArgumentNullException("value");
            {
                if (value is string)
                {
                    WaterMarkStyle obj = (WaterMarkStyle)TypeDescriptor.GetProperties(typeof(TextBox))["WaterMark"].GetValue(context.Instance);                    if (obj == null || obj.Color == Color.Empty)
                    {
                        return new WaterMarkStyle(value.ToString(), SystemColors.GrayText);
                    }
                    return new WaterMarkStyle(value.ToString(), obj.Color);
                }
            }
            return base.ConvertFrom(context, culture, value);
        }
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType==null)
            {
                throw new ArgumentNullException("destinationType");
            }
            if (value != null)
            {
                WaterMarkStyle wms = (WaterMarkStyle)value;
                if (destinationType == typeof(string))
                {
                    return wms.Text;
                }
                if (destinationType == typeof(InstanceDescriptor))
                {
                    ConstructorInfo ctor = typeof(WaterMarkStyle).GetConstructor(new Type[] { typeof(string), typeof(Color) });
                    if (ctor != null)
                    {
                        return new InstanceDescriptor(ctor, new object[] { wms.Text, wms.Color });
                    }                }
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            return TypeDescriptor.GetProperties(typeof(WaterMarkStyle), attributes).Sort(new string[] { "Text", "Color" });
        }
        public override bool GetPropertiesSupported(ITypeDescriptorContext context)
        {
            return true;
        }        public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
        {
            if (propertyValues == null)
            {
                throw new ArgumentNullException("propertyValues");
            }
            object obj2 = propertyValues["Text"];
            object obj3 = propertyValues["Color"];
            if (((obj2 == null) || (obj3 == null)) || (!(obj2 is string) || !(obj3 is Color)))
            {
                throw new ArgumentException("无效属性值!");
            }
            return new WaterMarkStyle(obj2 as string, (Color)obj3);
            
        }        public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
        {
            return true;
        }
    }以上是水印样式的类型定义及类型转换器的定义.请高手们给看下,如果还需要其他的代码我再往上加!

解决方案 »

  1.   

    已经说得很清楚了 :未将对象设置到对象的实例,所设置的变量为空值或没有取到值,一般出现在传递参数的时候出现这个问题,   waterMark = value;
    看看值有没有,另外 :控件名称与codebehind里面的没有对应,
    解决方法:
    (1)使用try..catch...finally捕捉错误,或直接用response.write()输出所取的变量值  (2)查看代码中是否存在未初始化的变量
      

  2.   

    功能简单一点的可以直接用ExpandableObjectConverter。public class MyTextBox : TextBox
    {
        WaterMarkStyle waterMark = new WaterMarkStyle("", SystemColors.GrayText);
        /// <summary>
        /// 获取或设置控件的水印效果.
        /// </summary>
        public WaterMarkStyle WaterMark
        {
            get { return waterMark; }
            set
            {
                if (waterMark != value)
                {
                    waterMark = value;
                }
            }
        }    /// <summary>
        /// 可编辑控件的水印样式
        /// </summary>
        [TypeConverter(typeof(ExpandableObjectConverter))]  //<---
        public class WaterMarkStyle
        {
            public WaterMarkStyle() : this("", SystemColors.GrayText)
            {
            }
            public WaterMarkStyle(string text, Color color) 
            {
                this.Text = text;
                this.Color = color;
            }        /// <summary>  获取或设置水印文本. </summary>
            public string Text { get; set;}        /// <summary> 获取或设置水印颜色. </summary>
            public Color Color {get; set;}        #region 重写判等方法
            public override bool Equals(object obj)
            {
                return this == (obj as WaterMarkStyle);
            }
            public override int GetHashCode()
            {
                return (this.Text ?? "").GetHashCode() ^ this.Color.GetHashCode();
            }
            public override string ToString()
            {
                return this.Text;
            }
            public static bool operator ==(WaterMarkStyle wm1, WaterMarkStyle wm2)
            {
                if (object.ReferenceEquals(wm1, null) || object.ReferenceEquals(wm2, null) )
                {
                    return object.ReferenceEquals(wm1, wm2);
                }
                return wm1.Text == wm2.Text && wm1.Color == wm2.Color;
            }
            public static bool operator !=(WaterMarkStyle wm1, WaterMarkStyle wm2)
            {
                return !(wm1 == wm2);
            }
            #endregion
            }
        }
    }如果要添加一些功能,象字符串转换,以及生成好看一些的代码,则可以提供自定义的TypeConverter:private class WaterMarkStyleTypeConverter : ExpandableObjectConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return destinationType == typeof(InstanceDescriptor) || base.CanConvertTo(context, destinationType);
        }
        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value is string)
            {
                Color color = SystemColors.GrayText;
                if (context != null && context.Instance != null && context.PropertyDescriptor != null)
                {
                    WaterMarkStyle old = context.PropertyDescriptor.GetValue(context.Instance) as WaterMarkStyle;
                    if (old != null && old.Color != SystemColors.GrayText) color = old.Color;
                }
                return new WaterMarkStyle((string)value, color);
            }
            return base.ConvertFrom(context, culture, value);
        }
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (value is WaterMarkStyle && destinationType == typeof(InstanceDescriptor))
            {
                ConstructorInfo ctor = typeof(WaterMarkStyle).GetConstructor(new Type[] { typeof(string), typeof(Color) });
                if (ctor != null)
                {
                    WaterMarkStyle wms = (WaterMarkStyle)value;
                    return new InstanceDescriptor(ctor, new object[] { wms.Text, wms.Color });
                }
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
      

  3.   

    应该先判断一下OnWaterMarkChanged是否为空:        /// <summary>
            /// 获取或设置控件的水印效果.
            /// </summary>
            [DefaultValue(typeof(WaterMarkStyle),"")]
            [Browsable(true)]
            public WaterMarkStyle WaterMark
            {
                get { return waterMark; }
                set 
                {
                    if (waterMark==value) return ;
                    waterMark = value;
                    if(OnWaterMarkChanged != null)
                        OnWaterMarkChanged(EventArgs.Empty);
                }
            }
      

  4.   

    LZ这是我以前做的水印图片
    这是生成验证码
    ----------------------- /**
             * IRequiresSessionState是一个空接口,仅用来
             * 标记handler是否对session拥有读写权
             */
            public void ProcessRequest(HttpContext context)
            {
                //1、建立画板
                Bitmap bitmap = new Bitmap(80, 30);
                //2、建立画布(绘图类)
                Graphics g = Graphics.FromImage(bitmap);
                //3、填充画布
                g.FillRectangle(Brushes.White, 0, 0, 80, 30);
                //4、设置字体
                Font f = new Font("隶书", 16);
                //5、在画布上写入字符
                string letters = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
                //6、定义变量,接受生成的随机字符串
                StringBuilder sb = new StringBuilder();
                //7、取字符
                Random r=new Random();
                //接受生成的单个随机字符
                string result = string.Empty;
                for (int i = 0; i < 4; i++)
                {
                    result=letters.Substring(r.Next(0, letters.Length - 1), 1);
                    sb.Append(result);
                    g.DrawString(result, f, Brushes.Red, i * 15, r.Next(0, 10));
                }
                //8、输出到响应流
                context.Response.ContentType = "image/Jpg";
                bitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
                //9、保存随机字符串到session
                context.Session["yanzheng"] = sb.ToString();
                //释放资源
                bitmap.Dispose();
                g.Dispose();
                context.Response.End();
            }
    --------------------
    这是水印
    -------- public void ProcessRequest(HttpContext context)
            {
                string path = context.Request.PhysicalPath;
                if (File.Exists(path))
                {
                    System.Drawing.Image image = Image.FromFile(path);
                    Graphics g = Graphics.FromImage(image);
                    Font f = new Font("楷体", 12);
                    g.DrawString("水印", f, Brushes.Red, 0, 0);
                    image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
                    g.Dispose();
                    image.Dispose();
                    context.Response.End();
                }
            }
      

  5.   

    这是上传的图片
     protected void btnUpload_Click(object sender, EventArgs e)
            {
                //获取图片路径
                string filePath = this.fuplImg.PostedFile.FileName;
                //获取文件名
                string fileName = this.fuplImg.FileName;
                //获取扩展名
                FileInfo info = new FileInfo(fileName);
                string type = info.Extension;
                //判断图片类型是否允许上传
                if (type == ".jpg" || type == ".bmp" || type == ".gif")
                {
                    //获取图片存储位置的物理路径
                    string path = Server.MapPath("Images//" + fileName);
                    //上传保存
                    this.fuplImg.PostedFile.SaveAs(path);
                    //加载图片
                    Image imgFile = new Image();
                    imgFile.Width = 200;
                    imgFile.Height = 150;
                    imgFile.ImageUrl = @"~/Images/" + fileName;
                    this.imgDiv.Controls.Add(imgFile);
                    Page.ClientScript.RegisterStartupScript(typeof(Page), "", "alert('上传成功!')", true);
                }
                else
                {
                    Page.ClientScript.RegisterStartupScript(typeof(Page), "", "alert('请检查上传图片的格式!')", true);
                }
            }
      

  6.   

    同意这个看法,其他人的回复都是答非所问。
    我在使用自定义事件的时候,都会加上是否为null的判断,所以从未出现过问题,这是个好习惯,任何可能出现null的地方,在使用前都要加为空判断。也许VS2008的编译机制和VS2010不一样了,导致你移植到VS2010去就报错。
      

  7.   

    首先谢谢大家的热情参与和讨论,下面我再说几点:
    1.关于事件检查为空的问题,是在OnWaterMarkChanged这个方法内部进行的:
    protected virtual void OnWaterMarkChanged(EventArgs e)
    {
       EventHandler handle=Events[MaterMarkChangedEvent] as EventHandler;
       if(handle!=null && handle.GetInvokedList().Length>0)
       {
          ... ...
       }
    }
    2.关于加try块调试,单从表面上看,错误的信息比较具体,但我可以比较确定的是和转换器的实现有很大关系,也就是说这个错误是在设计时支持里的,调试起来比较困难,因为你根本没法逐步的去调试,就算加上了Try块,我也不知道设计器会在什么时候执行这段代码并抛出异常.
    3.说下目前的情况吧,其他的都差不多了,就还剩下一个问题,就是我设置了WaterMark属性后,F5运行,没有任何问题,包括后台自动生成的代码也可以了,但是我在这个属性上右击选"重置"后,就会出现上面图片上所示的错误,
    理解是好理解,因为重置后WaterMark属性为空了呗,但我想知道,这个属性初始化定义的时候我是给了他值的,并且也使用了DefaultValueAttribute设置的默认值,就是空字符串,经过转换器后这个空串也应该被转换为一个空文本和GrayText颜色的WaterMarkStyle对象才对呀,怎么它就成了Null了呢?
    最后感谢一下zyloveyrf吧,因为我这个是WinForm的,不是Web,所谓的这个水印效果就是当文本框 没有文本且没有焦点是时候在上面显示一个提示文本,如果输入的内容或获取地焦点,则不显示这个问题能解决与否都没关系,希望大家都讨论一下!如果有不明白的请留言!
      

  8.   

    你的转换器写错了,问题应该在这段代码,自己调试去:        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                if (value != null)// return new WaterMarkStyle("",SystemColors.GrayText);// throw new ArgumentNullException("value");
                {
                    if (value is string)
                    {
                        WaterMarkStyle obj = (WaterMarkStyle)TypeDescriptor.GetProperties(typeof(TextBox))["WaterMark"].GetValue(context.Instance);                    if (obj == null || obj.Color == Color.Empty)
                        {
                            return new WaterMarkStyle(value.ToString(), SystemColors.GrayText);
                        }
                        return new WaterMarkStyle(value.ToString(), obj.Color);
                    }
                }
                return base.ConvertFrom(context, culture, value);
            }这句:WaterMarkStyle obj = (WaterMarkStyle)TypeDescriptor.GetProperties(typeof(TextBox))["WaterMark"].GetValue(context.Instance);
    ConvertFrom方法是用来将你的空字符串翻译到那个WaterMarkStyle 结构(注意,结构体不可能为空),你并未使用外部给的字符串来转换内部结构,而是用了自己的值来转换。if(obj == null)这个判断永不成立,当然,之前如果就是null的话WaterMarkStyle obj = ...这步就会报错,无法将null转换为WaterMarkStyle结构体,如果是类,不会报错,但是你这个是结构体,没有为null的结构体。
      

  9.   

    你原先代码是转换器ConvertFrom里有问题:
    WaterMarkStyle obj = (WaterMarkStyle)TypeDescriptor...我在3楼的代码,如果要支持重置,则WaterMarkStyle类不能是MyText的内嵌类(不知道Vistual Studio编辑器为什么要挑剔这个),要单独出来。
    [DefaultValue(typeof(WaterMarkStyle), "")]
    public WaterMarkStyle WaterMark
    {
       get { return waterMark; }
       ...
      

  10.   

    设置一个属性值就ok了   public WaterMarkStyle WaterMark
        {
            get { return waterMark; }
            set
            {
                if (waterMark != value)
                {
                    waterMark = value;
                }
            }
        }
      

  11.   

    谢谢qldsrx和gomoku了
    的确是这样,问题的确就出在WaterMarkStyle obj = (WaterMarkStyle)TypeDescriptor.GetProperties(typeof(TextBox))["WaterMark"].GetValue(context.Instance);
    因为重置后,TypeDescriptor.GetProperties(typeof(TextBox))["WaterMark"].GetValue(context.Instance);
    得到值为空,此处会引发异常,由于ConvertFrom方法没有返回值,所以会提示一个空引用的异常.