C# Winforms .Net 5:在执行派生 UC 的 Ctor 之前调用基本 UC 的 Load()

C# Winforms .Net 5: Load() of base UC is called before Ctor of derived UC is executed

我们已将项目从 .Net Framework 4.8 迁移到 .Net 5。
现在我们看到一个非常奇怪的问题:如果我们的应用程序的显示语言设置为不同于英语的语言,我们在应用程序启动期间会出现异常。如果它仍然是英文,一切都很好。

在具有 3 级继承的 UserControl 中引发异常:

问题是 UCImageObjectLoad 事件以某种方式在 UCImageObjectStandard 的构造函数之前执行:UCImageObject 调用的 Load() 方法一个虚拟 Init() 方法,它在 UCImageObjectStandard 中被覆盖。 UCImageObjectStandard.Init() 然后在 UCImageObjectStandard..ctor 之前执行,这当然是错误的,在我们的例子中这会导致异常。

我假设这是由直接或间接嵌套在 UCImageObjectUCImageObjectStandard。由于更改了语言,Text 属性 的更改可能会引发这些事件。编码文本是英文的,当我们的语言数据库在控件上设置其英文文本时,它们的内容不会改变,因为它们是相同的。但是其他语言的文本当然是不同的。

奇怪的是,这在 .Net Framework 4.8 中多年来一直运行良好。不过好像在.Net Core或者.Net 5中做了改动,使得一个UC的Load事件可能会比之前更早被调用

谁能告诉我发生了什么变化?
在使用继承的 类 的构造函数中调用重写方法是错误的。但是我们是否也应该避免在 Load 事件中调用重写的方法?

我已经尽我所能调试了它,包括单步执行 .Net 5 源文件。
执行失败的堆栈跟踪记录如下(我自己添加了帧号;未添加行号,因为它们在 .Net 文件中大多是错误的)。 错误的执行路径从堆栈帧 43 之上的某处开始。
我可以证明标签的文本(在堆栈跟踪帧 58 到 63 中使用)已经是非英语的了。

82   at SMCore.UIWF.UCImageObject.UCImageObject_Load(Object i_sender, EventArgs i_eventArgs)
81   at System.Windows.Forms.UserControl.OnLoad(EventArgs e)
80   at System.Windows.Forms.UserControl.OnCreateControl()
79   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
78   at System.Windows.Forms.Control.CreateControl()
77   at System.Windows.Forms.Control.WmShowWindow(Message& m)
76   at System.Windows.Forms.Control.WndProc(Message& m)
75   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
74   at System.Windows.Forms.ContainerControl.WndProc(Message& m)
73   at System.Windows.Forms.UserControl.WndProc(Message& m)
72   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
71   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
70   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam)
69   at Interop.User32.CreateWindowExW(WS_EX dwExStyle, Char* lpClassName, String lpWindowName, WS dwStyle, Int32 X, Int32 Y, Int32 nWidth, Int32 nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInst, Object lpParam)
68   at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
67   at System.Windows.Forms.Control.CreateHandle()
66   at System.Windows.Forms.Control.get_Handle()
65   at System.Windows.Forms.Control.get_ShowKeyboardCues()
64   at System.Windows.Forms.ControlPaint.CreateTextFormatFlags(Control control, ContentAlignment textAlign, Boolean showEllipsis, Boolean useMnemonic)
63   at System.Windows.Forms.Label.CreateTextFormatFlags(Size constrainingSize)
62   at System.Windows.Forms.Label.GetPreferredSizeCore(Size proposedConstraints)
61   at System.Windows.Forms.Control.GetPreferredSize(Size proposedSize)
60   at System.Windows.Forms.Label.GetPreferredSize(Size proposedSize)
59   at System.Windows.Forms.Control.get_PreferredSize()
58   at System.Windows.Forms.Label.SetBoundsCore(Int32 x, Int32 y, Int32 width, Int32 height, BoundsSpecified specified)
57   at System.Windows.Forms.Control.ScaleControl(SizeF factor, BoundsSpecified specified)
56   at System.Windows.Forms.Control.ScaleControl(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
55   at System.Windows.Forms.Control.Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
54   at System.Windows.Forms.Control.ScaleChildControls(SizeF includedFactor, SizeF excludedFactor, Control requestingControl, Boolean updateWindowFontIfNeeded)
53   at System.Windows.Forms.ContainerControl.Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
52   at System.Windows.Forms.Control.ScaleChildControls(SizeF includedFactor, SizeF excludedFactor, Control requestingControl, Boolean updateWindowFontIfNeeded)
51   at System.Windows.Forms.Control.Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
50   at System.Windows.Forms.Control.ScaleChildControls(SizeF includedFactor, SizeF excludedFactor, Control requestingControl, Boolean updateWindowFontIfNeeded)
49   at System.Windows.Forms.Control.Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
48   at System.Windows.Forms.Control.ScaleChildControls(SizeF includedFactor, SizeF excludedFactor, Control requestingControl, Boolean updateWindowFontIfNeeded)
47   at System.Windows.Forms.ContainerControl.Scale(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
46   at System.Windows.Forms.ContainerControl.PerformAutoScale(Boolean includedBounds, Boolean excludedBounds)
45   at System.Windows.Forms.ContainerControl.PerformNeededAutoScaleOnLayout()
44   at System.Windows.Forms.ContainerControl.OnLayoutResuming(Boolean performLayout)
43   at System.Windows.Forms.Control.ResumeLayout(Boolean performLayout)
42   at SMCore.UIWF.UCImageObjectStandard.InitializeComponent()
41   at SMCore.UIWF.UCImageObjectStandard..ctor()
40   at SMCore.UIWF.UCImgObjStd_Circle..ctor()
29   at SMCore.UIWF.UCImageObject.Create(EnumType i_type, UCImageObject& io_ucImageObject)
28   at SMCore.UIWF.UCImageObject.ShowData(UCImageObject& io_ucImageObject, CImageObject i_imageObject)
27   at DI.UIWF.UCJobCreatorSettings.ucImageObjectList_SelectionChanged(Object i_oSender, EventArgs i_oEventArgs)
26   at SMCore.UIWF.UCImageObjectList.OnSelectionChanged(Int32 i_imageObjectID)
25   at SMCore.UIWF.UCImageObjectList.dgvImageObjects_SelectionChanged(Object i_sender, EventArgs i_eventArgs)
24   at SMCore.UIWF.UCImageObjectList.ShowData(CImageObjectList i_imageObjectList)
23   at DI.UIWF.UCJobCreatorSettings.Init(CImageObjectList i_oImageObjectList, IList`1 i_listoImageAcquisitionDeviceInfo)
22   at DI_JobEditor.UIWF.frmMain.Init_UI_Settings()
21   at DI_JobEditor.UIWF.frmMain.frmMain_Load(Object i_oSender, EventArgs i_oEventArgs)
20   at System.Windows.Forms.Form.OnLoad(EventArgs e)
19   at System.Windows.Forms.Form.OnCreateControl()
18   at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
17   at System.Windows.Forms.Control.CreateControl()
16   at System.Windows.Forms.Control.WmShowWindow(Message& m)
15   at System.Windows.Forms.Control.WndProc(Message& m)
14   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
13   at System.Windows.Forms.ContainerControl.WndProc(Message& m)
12   at System.Windows.Forms.Form.WmShowWindow(Message& m)
11   at System.Windows.Forms.Form.WndProc(Message& m)
10   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
09   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
08   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam)
07   at Interop.User32.ShowWindow(IntPtr hWnd, SW nCmdShow)
06   at System.Windows.Forms.Control.SetVisibleCore(Boolean value)
05   at System.Windows.Forms.Form.SetVisibleCore(Boolean value)
04   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(msoloop reason, ApplicationContext context)
03   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(msoloop reason, ApplicationContext context)
02   at System.Windows.Forms.Application.Run(Form mainForm)
01   at DI_JobEditor.UIWF.Program.Main(String[] args)

编辑
我发现代码执行因英语或非英语语言而异的地方。它在堆栈帧 56 中:
System.Windows.Forms.Control.ScaleControl(SizeF includedFactor, SizeF excludedFactor, Control requestingControl)
该行是 10131 (.Net 5.0.13):
if (includedSpecified != BoundsSpecified.None)

如果语言仍然是英语,则标签的文本不会更改(标签是 UCTolerance 的子项,后者是 UCImageObjectStandard 的子项)。因此,标签的大小也保持不变 (Label.AutoSize=true)。然后就不需要缩放控件了(UC.AutoScaleMode = Dpi).

这意味着有 3 种方法可以避免不需要的执行路径:

  1. 如果 UCTolerance 的 AutoScaleMode 不是 DPI,则条件
    if (AutoScaleMode != AutoScaleMode.None && AutoScaleMode != AutoScaleMode.Inherit)
    in ContainerControl.PerformAutoScale(bool,bool) 将为假,所有 Scale 方法将永远不会被执行。
  2. 如果 Label 的 AutoSize 不正确,则控件不会调整大小,并且可能不需要缩放它。
  3. 如果文本没有改变,则上述条件
    if (includedSpecified != BoundsSpecified.None)
    将是错误的,因为由 Control.RequiredScaling(第 10116 行)设置的 includedSpecified 将保持 None 而不会变为 Size.

#3 没有选项,因为文本必须根据所选语言进行设置。 但我可以轻松实现#1 和#2,因为我可以使用固定大小的标签,并在需要时在 UCToleranceLayout 事件中调整它们的大小。而且我可以设置 UCTolerance.AutoScaleMode=None,因为我们不使用 AutoScale 反正。

但问题依然存在,见上文。

编辑 https://github.com/dotnet/winforms/files/7962713/di-software.zip

处的重现解决方案

根据我在 https://github.com/dotnet/winforms/issues/6565 GitHub 的报告中的回答,这是 .NET 5 中的错误,已在 .NET 6 和 7 中修复。