winforms NumericUpDown包含在TextBoxBase控件的迭代中

afdcj2ne  于 2023-10-23  发布在  其他
关注(0)|答案(2)|浏览(114)

我有一个WinForm与NumericUpDowns(NUD),正常的文本框和MaskedTextBoxes。应该清除文本框,我尝试了以下代码:

For Each txtBox As TextBoxBase In Controls.OfType(Of TextBoxBase)
    txtBox.Clear()
Next

这会清除文本框,但也会清除NUD的文本,这是不应该的。
使用CtrlClick查找NumericUpDown的父代,据我所知,它甚至没有继承自TextBoxBase
不过,它们还是被清除了,所以我想在清除txtBox之前添加一个条件来检查它是否不是NUD。我试着检查:

If txtBox.GetType() <> GetType(NumericUpDown) Then
    txtBox.Clear()
End If

以及以下条件:

txtBox.GetType() IsNot GetType(NumericUpDown)
TypeOf txtBox IsNot NumericUpDown

所有这些都是UpDownBase而不是NUD。没有什么比得上False

但是在调试时,我使用

Debug.Print(txtBox.GetType().ToString())

这是System.Windows.Forms.UpDownBase+UpDownEdit。没有一个类型叫做UpDownEdit,我也不知道我应该如何理解+
什么条件工作,然后只是比较类型名称字符串:

txtBox.GetType().ToString().Contains(GetType(UpDownBase).ToString())

但这不是一个好方法,所以:
考虑到上述挑战,检查txtBox中的对象是否真的是NUD的“正确”方法是什么?
这个问题出现的方法是下面的递归子:

Public Sub ClearInputControls(elements As ControlCollection)
    ' Recurse into containers.
    For Each pnl As Panel In elements.OfType(Of Panel)
        ClearInputControls(pnl.Controls)
    Next
    For Each cntr As ContainerControl elements.OfType(Of ContainerControl)
        ClearInputControls(cntr.Controls)
    Next
    ' Reset controls.
    For Each txtBox As TextBoxBase In elements.OfType(Of TextBoxBase)
        txtBox.Clear()
    Next
    For Each count As NumericUpDown In elements.OfType(Of NumericUpDown)
        count.Value = count.Minimum
    Next
    For Each cbox As ListControl In elements.OfType(Of ListControl)
        cbox.SelectedIndex = -1
    Next
End Sub

如果NUD中的值从其默认值更改,则不会清除它们。
但这似乎是值的任何 * 更改 * 都会提示NUD再次显示该数字,因此每个循环的第二个仅在值与最小值(默认值)不同时更新NUD。
当它在清除循环之后出现时,由于通过更改进行更新,值再次显示。

wz1wpwve

wz1wpwve1#

我现在找到了问题所在--评论是正确的:它不在我最初在问题中提供的一个循环中。
这个循环在一个sub中被调用,该sub递归到所有控件OfType ContainerControl中,以清除SplitContainers等中的所有文本框:

Public Sub ClearInputControls(elements As ControlCollection)
    ' Recurse into containers.
    For Each cntr As ContainerControl elements.OfType(Of ContainerControl)
        ClearInputControls(cntr.Controls)
    Next
    ' Reset controls.
    For Each txtBox As TextBoxBase In elements.OfType(Of TextBoxBase)
        txtBox.Clear()
    Next
End Sub

问题是,NumericUpDown(NUD)是OfType ContainerControl(CC),它们包含的是OfType TextBoxBase
因此,for each循环“打开”一个NUD作为CC。此CC“保存”一个TextBoxBase,然后由for循环清除。
当使用Ctrl+Click查看NUD的定义时,它只把我带到了UpDownBase,而不是进一步向下,但是现在查看NUD类文档,它确实从CC继承。
因此,解决方案是将for each循环改为仅在SplitContainers上执行:

For Each cntr As SplitContainers elements.OfType(Of SplitContainers)
    ClearInputControls(cntr.Controls)
Next

TextBoxBases上的for循环可以保持原样。
或者,为了继续使用CC,可以在递归之前检查OfType NumericUpDown

For Each cntr As ContainerControl elements.OfType(Of ContainerControl)
    If TypeOf cntr IsNot NumericUpDown Then
        ClearInputControls(cntr.Controls)
    End If
Next
qni6mghb

qni6mghb2#

不需要执行单独的循环来遍历给定集合中的不同类型的控件。在一个过程中,循环执行递归部分,而其他循环处理目标控件。分开解决问题。
首先,创建一个recursive方法,获取给定父控件的所有子控件。

**注:**选项StrictExplicitInferOn

Private Function GetChildren(parent As Control) As IEnumerable(Of Control)
    Dim ctrls = parent.Controls.Cast(Of Control)
    Return ctrls.SelectMany(Function(c) GetChildren(c)).Concat(ctrls)
End Function

第二个是调用方方法,它遍历子控件,处理目标类型,并跳过其余部分。

Private Sub ClearInputControls(parent As Control)
    For Each element In GetChildren(parent)
        If TypeOf element Is NumericUpDown Then
            Dim nud = DirectCast(element, NumericUpDown)
            nud.Value = nud.Minimum
        ElseIf TypeOf element Is ListControl Then
            DirectCast(element, ListControl).SelectedIndex = -1
        ElseIf TypeOf element Is DateTimePicker Then
            DirectCast(element, DateTimePicker).Value = Now
        ElseIf TypeOf element Is TextBoxBase Then
            If Not element.GetType().IsNested Then element.Text = ""
            ' Or...
            ' If TypeOf element.Parent IsNot NumericUpDown Then element.Text = ""                
        End If
    Next
End Sub

更好的是,您可以扩展类型Control,并将递归例程作为extension method。在vb.net中,您需要在Module中拥有扩展方法,并使用<Extension>属性装饰它们。

Imports System.Runtime.CompilerServices

Module ControlExtensions
    <Extension>
    Public Function GetChildren(parent As Control) As IEnumerable(Of Control)
        Dim ctrls = parent.Controls.Cast(Of Control)
        Return ctrls.SelectMany(Function(c) GetChildren(c)).Concat(ctrls)
    End Function
End Module

现在,项目中所有类型为Control的对象都具有此方法。

Private Sub ClearInputControls(parent As Control)
    For Each element In parent.GetChildren()
        ' ... etc.
    Next
End Sub

此外,如果需要的话,您可以有一个通用的扩展方法来获取特定类型的控件。

Module ControlExtensions
    <Extension>
    Public Function GetChildren(parent As Control) As IEnumerable(Of Control)
        Dim ctrls = parent.Controls.Cast(Of Control)
        Return ctrls.SelectMany(Function(c) GetChildren(c)).Concat(ctrls)
    End Function

    <Extension>
    Public Function GetChildren(Of T)(parent As Control) As IEnumerable(Of T)
        Dim ctrls = parent.Controls.Cast(Of Control)
        Return ctrls.SelectMany(Function(ctrl) GetChildren(Of T)(ctrl)).Concat(ctrls.OfType(Of T))
    End Function
End Module

就这么叫吧。

For Each element In parent.GetChildren(Of GroupBox)
    ' ...
Next

相关问题