自定义控件属性的初始化问题

jbo126 2011-08-26 01:53:53
我有一个自定义的控件,是扩展标准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;
}
}



以上是水印样式的类型定义及类型转换器的定义.请高手们给看下,如果还需要其他的代码我再往上加!

...全文
546 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
qldsrx 2011-08-26
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 ojlovecd 的回复:]
应该先判断一下OnWaterMarkChanged是否为空:

C# code


/// <summary>
/// 获取或设置控件的水印效果.
/// </summary>
[DefaultValue(typeof(WaterMarkStyle),"")]
[Browsable(true)]
……
[/Quote]
同意这个看法,其他人的回复都是答非所问。
我在使用自定义事件的时候,都会加上是否为null的判断,所以从未出现过问题,这是个好习惯,任何可能出现null的地方,在使用前都要加为空判断。也许VS2008的编译机制和VS2010不一样了,导致你移植到VS2010去就报错。
  • 打赏
  • 举报
回复
楼主这种错误就是传参数的时候出的错误

再传参数的时候都加个判断就可以了
萧炎 2011-08-26
  • 打赏
  • 举报
回复
这是上传的图片
 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);
}
}
萧炎 2011-08-26
  • 打赏
  • 举报
回复
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();
}
}
jbo126 2011-08-26
  • 打赏
  • 举报
回复
谢谢qldsrx和gomoku了
的确是这样,问题的确就出在WaterMarkStyle obj = (WaterMarkStyle)TypeDescriptor.GetProperties(typeof(TextBox))["WaterMark"].GetValue(context.Instance);
因为重置后,TypeDescriptor.GetProperties(typeof(TextBox))["WaterMark"].GetValue(context.Instance);
得到值为空,此处会引发异常,由于ConvertFrom方法没有返回值,所以会提示一个空引用的异常.
萧炎 2011-08-26
  • 打赏
  • 举报
回复
LZ为了你的1点过发帖的工作精神 先帮顶一个来
你的选择B 2011-08-26
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 gefangliang 的回复:]

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

(2)查看代码中是否存在未……
[/Quote]

是个办法
调试,看具体是那个地方出现了问题
我姓区不姓区 2011-08-26
  • 打赏
  • 举报
回复
应该先判断一下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);
}
}

gomoku 2011-08-26
  • 打赏
  • 举报
回复
功能简单一点的可以直接用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);
}
}
心灵彩虹 2011-08-26
  • 打赏
  • 举报
回复
已经说得很清楚了 :未将对象设置到对象的实例,所设置的变量为空值或没有取到值,一般出现在传递参数的时候出现这个问题, waterMark = value;
看看值有没有,另外 :控件名称与codebehind里面的没有对应,
解决方法:
(1)使用try..catch...finally捕捉错误,或直接用response.write()输出所取的变量值

  (2)查看代码中是否存在未初始化的变量



josephSC 2011-08-26
  • 打赏
  • 举报
回复
先站个沙发,顶~
LMAOhuaNL 2011-08-26
  • 打赏
  • 举报
回复
设置一个属性值就ok了

public WaterMarkStyle WaterMark
{
get { return waterMark; }
set
{
if (waterMark != value)
{
waterMark = value;
}
}
}
gomoku 2011-08-26
  • 打赏
  • 举报
回复
你原先代码是转换器ConvertFrom里有问题:
WaterMarkStyle obj = (WaterMarkStyle)TypeDescriptor...

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

qldsrx 2011-08-26
  • 打赏
  • 举报
回复
你的转换器写错了,问题应该在这段代码,自己调试去:

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的结构体。
jbo126 2011-08-26
  • 打赏
  • 举报
回复
首先谢谢大家的热情参与和讨论,下面我再说几点:
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,所谓的这个水印效果就是当文本框 没有文本且没有焦点是时候在上面显示一个提示文本,如果输入的内容或获取地焦点,则不显示

这个问题能解决与否都没关系,希望大家都讨论一下!如果有不明白的请留言!

110,538

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧