拖拽自定义ProgressBar到Form1设计器时参数无效异常排查
自定义ProgressBar拖拽设计器触发异常问题
首次把自定义ProgressBar从工具箱拖到Form1设计器时能正常运行,但现在每次拖拽都会触发异常,而且完全没改过代码。
异常触发点
异常发生在DrawStringIfNeeded方法的第165行:g.DrawString(text, TextFont, _textColourBrush, location);,错误提示为参数无效。
问题根源
- Font对象异常:
TextFont属性的默认Font对象在设计器反复序列化、控件销毁重建的过程中,可能被意外释放,导致绘制时引用无效对象。 - 画笔对象提前释放:
TextColor和ProgressColor的setter中直接Dispose旧画笔,但设计器环境下可能多次调用该方法,或者在控件未完全初始化时就触发绘制,导致画笔已处于释放状态。 - 设计器模式下无特殊处理:控件在设计器中绘制时,没有区分运行时和设计时逻辑,复杂的绘制操作容易触发环境兼容性问题。
修复方案
1. 重构TextFont属性,避免无效引用
把自动属性改成带字段的属性,增加空值检查和资源释放逻辑,防止Font对象失效:
private Font _textFont; [Description("进度条上文本的字体"), Category("附加选项")] public Font TextFont { get => _textFont ?? (_textFont = new Font(FontFamily.GenericSerif, 14, FontStyle.Bold | FontStyle.Italic)); set { if (_textFont != value) { // 仅释放非默认的自定义字体 if (_textFont != null && !_textFont.Equals(new Font(FontFamily.GenericSerif, 14, FontStyle.Bold | FontStyle.Italic))) { _textFont.Dispose(); } _textFont = value; Invalidate(); } } }
2. 修正画笔初始化逻辑,避免共享系统画笔
不要直接使用Brushes.Black这类系统共享画笔,改为创建独立的SolidBrush实例,防止系统画笔被意外释放:
private SolidBrush _textColourBrush = new SolidBrush(Color.Black); [Category("附加选项")] public Color TextColor { get => _textColourBrush.Color; set { if (_textColourBrush.Color != value) { _textColourBrush.Dispose(); _textColourBrush = new SolidBrush(value); Invalidate(); } } } private SolidBrush _progressColourBrush = new SolidBrush(Color.LightGreen); [Category("附加选项"), Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] public Color ProgressColor { get => _progressColourBrush.Color; set { if (_progressColourBrush.Color != value) { _progressColourBrush.Dispose(); _progressColourBrush = new SolidBrush(value); Invalidate(); } } }
3. 绘制前增加有效性检查,捕获异常
在DrawStringIfNeeded方法中,提前检查Font、画笔的有效性,捕获参数异常避免设计器崩溃:
private void DrawStringIfNeeded(Graphics g) { if (VisualMode == ProgressBarDisplayMode.NoText || TextFont == null || _textColourBrush == null) { return; } string text = _textToDraw; if (string.IsNullOrEmpty(text)) { return; } try { SizeF len = g.MeasureString(text, TextFont); Point location = new Point((Width / 2) - (int)len.Width / 2, (Height / 2) - (int)len.Height / 2); g.DrawString(text, TextFont, _textColourBrush, location); } catch (ArgumentException) { // 捕获参数无效异常,触发重绘 Invalidate(); } }
4. 区分设计器与运行时绘制逻辑
在OnPaint方法中判断是否处于设计器模式,设计器下使用基础绘制逻辑,避免复杂操作触发异常:
protected override void OnPaint(PaintEventArgs e) { if (e.Graphics == null) { return; } // 设计器模式下用默认绘制逻辑 if (DesignMode) { base.OnPaint(e); return; } Graphics g = e.Graphics; DrawProgressBar(g); DrawStringIfNeeded(g); }
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

