图表中出现异常后未调用 Winforms OnPaint
Winforms OnPaint not being called after Exception in Chart
您好互联网开发者!
如果轴或显示的点取不应该有的值,Winforms.DataVisualization.Charting.Chart 会将自己画成一个大红叉。
当 OnPaint 方法内部发生异常时,就会发生这种情况。本主题的其他问题建议创建一个子类并覆盖 OnPaint。这就是我所做的,这就是我如何找出导致问题的原因。
有趣的问题是:如何摆脱这种错误状态?
我尝试将属性设置为有效值并清除所有内容,调用 Invalidate() 和通常的事情。发生错误后,OnPaint 方法再也不会被调用。
Winforms 中是否有某种行为会导致这种情况?控件可以标记为 "failed beyond recover" 吗?
当我将控件移到失败状态图上方时,红色 x 被重新绘制,因此它被绘制,但不是单独绘制。
我想知道我在这里缺少哪种细节知识。
PS: SetStyle 没有帮助,在错误发生之前调用了 OnPaint 并且实施它并没有解决它。
附录 A:重新编译
我反汇编了整个Winforms.DataVisualization.Charting程序集的源代码并重新编译。通过注释掉抛出;在图表 OnPaint 中,该方法不会留下预期,而只是绘制一个 "error has occured"-图像本身。
在这种情况下,重绘不会永远禁用。
如果控件抛出异常,Winforms 似乎会从绘制顺序中移除控件。
调试提取的程序集源版本使我们对问题有了一些了解。首先,根据提供的 link @TnTinMn
,控件在抛出异常后被永远排除在 OnPaint 之外是正常行为。
然而,图表本身在 OnPaint 中有一个代码路径,当 OnPaint 为 运行 时,该代码路径不会重置用于阻止失效的内部字段。
以下代码是捕获OnPaint异常并使用反射重置私有字段的图表的子类。因此,如果图表进入错误状态,可以通过设置有效值并简单地重绘来重置它。
此外,在创建图表控件后,请确保设置轴的最小值和最大值属性,即使是未使用的。属性 setter 不仅仅是将值放入字段。对我来说,这有助于避免抛出异常。
/// <summary>
/// Subclass catches exception thrown from base.OnPaint and thus prevents the red x.
/// Resets base classes private field "disableInvalidates" via reflection to reallow invalidation.
/// </summary>
public class ChartSubclass : Chart
{
private readonly System.Reflection.FieldInfo disableInvalidatesField;
public ChartSubclass()
{
this.disableInvalidatesField = typeof(Chart).GetField("disableInvalidates",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.SetField |
System.Reflection.BindingFlags.Instance);
}
protected override void OnPaint(PaintEventArgs e)
{
try
{
base.OnPaint(e);
}
catch (Exception ex)
{
this.disableInvalidatesField.SetValue(this, false);
this.DrawErrorState(e, ex);
}
}
/// <summary>
/// Draws error message.
/// </summary>
private void DrawErrorState(PaintEventArgs e, Exception ex)
{
var graphics = e.Graphics;
graphics.FillRectangle(Brushes.White, 0, 0, base.Width, base.Height);
var layoutRectangle = new RectangleF(3f, 3f, base.Width - 6, base.Height - 6);
using (var stringFormat = new StringFormat())
{
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
graphics.DrawString(ex.Message, this.Font, Brushes.Black, layoutRectangle, stringFormat);
}
}
}
您好互联网开发者!
如果轴或显示的点取不应该有的值,Winforms.DataVisualization.Charting.Chart 会将自己画成一个大红叉。
当 OnPaint 方法内部发生异常时,就会发生这种情况。本主题的其他问题建议创建一个子类并覆盖 OnPaint。这就是我所做的,这就是我如何找出导致问题的原因。
有趣的问题是:如何摆脱这种错误状态? 我尝试将属性设置为有效值并清除所有内容,调用 Invalidate() 和通常的事情。发生错误后,OnPaint 方法再也不会被调用。
Winforms 中是否有某种行为会导致这种情况?控件可以标记为 "failed beyond recover" 吗? 当我将控件移到失败状态图上方时,红色 x 被重新绘制,因此它被绘制,但不是单独绘制。
我想知道我在这里缺少哪种细节知识。
PS: SetStyle 没有帮助,在错误发生之前调用了 OnPaint 并且实施它并没有解决它。
附录 A:重新编译 我反汇编了整个Winforms.DataVisualization.Charting程序集的源代码并重新编译。通过注释掉抛出;在图表 OnPaint 中,该方法不会留下预期,而只是绘制一个 "error has occured"-图像本身。 在这种情况下,重绘不会永远禁用。
如果控件抛出异常,Winforms 似乎会从绘制顺序中移除控件。
调试提取的程序集源版本使我们对问题有了一些了解。首先,根据提供的 link @TnTinMn
,控件在抛出异常后被永远排除在 OnPaint 之外是正常行为。然而,图表本身在 OnPaint 中有一个代码路径,当 OnPaint 为 运行 时,该代码路径不会重置用于阻止失效的内部字段。
以下代码是捕获OnPaint异常并使用反射重置私有字段的图表的子类。因此,如果图表进入错误状态,可以通过设置有效值并简单地重绘来重置它。
此外,在创建图表控件后,请确保设置轴的最小值和最大值属性,即使是未使用的。属性 setter 不仅仅是将值放入字段。对我来说,这有助于避免抛出异常。
/// <summary>
/// Subclass catches exception thrown from base.OnPaint and thus prevents the red x.
/// Resets base classes private field "disableInvalidates" via reflection to reallow invalidation.
/// </summary>
public class ChartSubclass : Chart
{
private readonly System.Reflection.FieldInfo disableInvalidatesField;
public ChartSubclass()
{
this.disableInvalidatesField = typeof(Chart).GetField("disableInvalidates",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.SetField |
System.Reflection.BindingFlags.Instance);
}
protected override void OnPaint(PaintEventArgs e)
{
try
{
base.OnPaint(e);
}
catch (Exception ex)
{
this.disableInvalidatesField.SetValue(this, false);
this.DrawErrorState(e, ex);
}
}
/// <summary>
/// Draws error message.
/// </summary>
private void DrawErrorState(PaintEventArgs e, Exception ex)
{
var graphics = e.Graphics;
graphics.FillRectangle(Brushes.White, 0, 0, base.Width, base.Height);
var layoutRectangle = new RectangleF(3f, 3f, base.Width - 6, base.Height - 6);
using (var stringFormat = new StringFormat())
{
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
graphics.DrawString(ex.Message, this.Font, Brushes.Black, layoutRectangle, stringFormat);
}
}
}