как получить все дочерние элементы управления формы windows forms определенного типа (кнопка/текстовое поле)?

<p>мне нужно получить все элементы управления в форме, которые имеют тип х. я почти уверен, что когда-то видел этот код, который использовал что-то вроде этого:</p><pre><code>dim ctrls() as control ctrls = me.controls(gettype(textbox)) </code></pre><p>я знаю, что могу перебрать все элементы управления, заставляя детей использовать рекурсивную функцию, но есть ли что-то более простое или более простое, например, следующее?</p><pre><code>dim ctrls = from ctrl in me.controls where ctrl.gettype is textbox </code></pre>

<p>вот еще один вариант для вас. я протестировал его, создав образец приложения, затем поместил groupbox и groupbox в начальный groupbox. внутри вложенного groupbox я поместил 3 элемента управления textbox и кнопку. это код, который я использовал (даже включает рекурсию, которую вы искали)</p><pre><code>public ienumerable<control> getall(control control,type type) { var controls = control.controls.cast<control>(); return controls.selectmany(ctrl => getall(ctrl,type)) .concat(controls) .where(c => c.gettype() == type); } </code></pre><p>чтобы проверить это в событии загрузки формы, я хотел подсчитать все элементы управления внутри исходного groupbox</p><pre><code>private void form1\_load(object sender, eventargs e) { var c = getall(this,typeof(textbox)); messagebox.show("total controls: " + c.count()); } </code></pre><p>и каждый раз он возвращал правильное количество, так что я думаю, что это будет отлично работать для того, что вы ищете :)</p>

<p>в c # (поскольку вы пометили его как таковой) вы можете использовать выражение linq, например:</p><pre><code>list<control> c = controls.oftype<textbox>().cast<control>().tolist(); </code></pre><hr><p><strong>редактировать для рекурсии:</strong></p><p>в этом примере вы сначала создаете список элементов управления, а затем вызываете метод для его заполнения. поскольку метод рекурсивный, он не возвращает список, он просто обновляет его.</p><pre><code>list<control> controllist = new list<control>(); private void getallcontrols(control container) { foreach (control c in container.controls) { getallcontrols(c); if (c is textbox) controllist.add(c); } } </code></pre><p>это может быть возможно сделать в одном операторе linq, используя функцию <code>descendants</code>, хотя я не настолько знаком с этим. смотрите <a href="http://mutable.net/blog/archive/2008/05/23/using-linq-to-objects-for-recursion.aspx">эту страницу</a> для получения дополнительной информации об этом.</p><p><strong>изменить 2, чтобы вернуть коллекцию:</strong></p><p>как предложил @profk, метод, который просто возвращает желаемые элементы управления, вероятно, является лучшей практикой. чтобы проиллюстрировать это, я изменил код следующим образом:</p><pre><code>private ienumerable<control> getalltextboxcontrols(control container) { list<control> controllist = new list<control>(); foreach (control c in container.controls) { controllist.addrange(getalltextboxcontrols(c)); if (c is textbox) controllist.add(c); } return controllist; } </code></pre>

<p>это улучшенная версия рекурсивного метода getallcontrols (), который фактически работает с частными переменными:</p><pre><code> private void test() { list<control> alltextboxes = getallcontrols(this); } private list<control> getallcontrols(control container, list<control> list) { foreach (control c in container.controls) { if (c is textbox) list.add(c); if (c.controls.count > 0) list = getallcontrols(c, list); } return list; } private list<control> getallcontrols(control container) { return getallcontrols(container, new list<control>()); } </code></pre>

<p>я объединил кучу предыдущих идей в один метод расширения. преимущества здесь в том, что вы получаете обратно верный перечислимый тип, плюс наследование корректно обрабатывается <code>oftype()</code>.</p><pre><code>public static ienumerable<t> findallchildrenbytype<t>(this control control) { ienumerable<control> controls = control.controls.cast<control>(); return controls .oftype<t>() .concat<t>(controls.selectmany<control, t>(ctrl => findallchildrenbytype<t>(ctrl))); } </code></pre>

<p>вы можете использовать запрос linq, чтобы сделать это. это будет запрашивать все в форме, которая имеет тип textbox</p><pre><code>var c = from controls in this.controls.oftype<textbox>() select controls; </code></pre>

<p>это может быть древняя техника, но она работает как шарм. я использовал рекурсию, чтобы изменить цвет всех меток элемента управления. работает отлично.</p><pre><code>internal static void changecontrolcolour(control f, color color) { foreach (control c in f.controls) { // messagebox.show(c.gettype().tostring()); if (c.haschildren) { changecontrolcolour(c, color); } else if (c is label) { label lll = (label)c; lll.forecolor = color; } } } </code></pre>

<p>я хотел бы изменить ответ psychocoders: поскольку пользователь хочет получить все элементы управления определенного типа, мы можем использовать дженерики следующим образом:</p><pre class="lang-cs prettyprint-override"><code> public ienumerable<t> findcontrols<t>(control control) where t : control { // we can't cast here because some controls in here will most likely not be <t> var controls = control.controls.cast<control>(); return controls.selectmany(ctrl => findcontrols<t>(ctrl)) .concat(controls) .where(c => c.gettype() == typeof(t)).cast<t>(); } </code></pre><p>таким образом, мы можем вызвать функцию следующим образом:</p><pre class="lang-cs prettyprint-override"><code>private void form1\_load(object sender, eventargs e) { var c = findcontrols<textbox>(this); messagebox.show("total controls: " + c.count()); } </code></pre>

<p>не забывайте, что вы можете также иметь textbox в других элементах управления <em>кроме элементов управления</em> контейнера. вы даже можете добавить textbox в picturebox.</p><p>так что вы также должны проверить, если</p><pre><code>somecontrol.haschildren = true </code></pre><p>в любой рекурсивной функции.</p><p>вот результат, который я получил от макета для тестирования этого кода:</p><pre><code>textbox13 parent = panel5 textbox12 parent = panel5 textbox9 parent = panel2 textbox8 parent = panel2 textbox16 parent = panel6 textbox15 parent = panel6 textbox14 parent = panel6 textbox10 parent = panel3 textbox11 parent = panel4 textbox7 parent = panel1 textbox6 parent = panel1 textbox5 parent = panel1 textbox4 parent = form1 textbox3 parent = form1 textbox2 parent = form1 textbox1 parent = form1 tbtest parent = mypicbox </code></pre><p>попробуйте это с<strong> <em>one button</em> </strong>и<strong> one richtextbox </strong>на форме.</p><pre><code>option strict on option explicit on option infer off public class form1 private sub button1\_click(byval sender as system.object, byval e as system.eventargs) handles button1.click dim pb as new picturebox pb.name = "mypicbox" pb.backcolor = color.goldenrod pb.size = new size(100, 100) pb.location = new point(0, 0) dim tb as new textbox tb.name = "tbtest" pb.controls.add(tb) me.controls.add(pb) dim textboxlist as new list(of control) textboxlist = getallcontrols(of textbox)(me) dim sb as new system.text.stringbuilder for index as integer = 0 to textboxlist.count - 1 sb.append(textboxlist.item(index).name & " parent = " & textboxlist.item(index).parent.name & system.environment.newline) next richtextbox1.text = sb.tostring end sub private function getallcontrols(of t)(byval searchwithin as control) as list(of control) dim returnlist as new list(of control) if searchwithin.haschildren = true then for each ctrl as control in searchwithin.controls if typeof ctrl is t then returnlist.add(ctrl) end if returnlist.addrange(getallcontrols(of t)(ctrl)) next elseif searchwithin.haschildren = false then for each ctrl as control in searchwithin.controls if typeof ctrl is t then returnlist.add(ctrl) end if returnlist.addrange(getallcontrols(of t)(ctrl)) next end if return returnlist end function end class </code></pre>

<p>используя отражение:</p><pre><code>// return a list with all the private fields with the same type list<t> getallcontrolswithtypefromcontrol<t>(control parentcontrol) { list<t> retvalue = new list<t>(); system.reflection.fieldinfo[] fields = parentcontrol.gettype().getfields(system.reflection.bindingflags.nonpublic | system.reflection.bindingflags.instance); foreach (system.reflection.fieldinfo field in fields) { if (field.fieldtype == typeof(t)) retvalue.add((t)field.getvalue(parentcontrol)); } } list<textbox> ctrls = getallcontrolswithtypefromcontrol<textbox>(this); </code></pre>

<p>чистое и простое решение (c #): </p><pre><code>static class utilities { public static list<t> getallcontrols<t>(this control container) where t : control { list<t> controls = new list<t>(); if (container.controls.count > 0) { controls.addrange(container.controls.oftype<t>()); foreach (control c in container.controls) { controls.addrange(c.getallcontrols<t>()); } } return controls; } } </code></pre><p>получить все текстовые поля:</p><pre><code>list<textbox> textboxes = mycontrol.getallcontrols<textbox>(); </code></pre>

<pre><code>public list<control> getallchildcontrols(control root, type filtertype = null) { list<control> allchilds = new list<control>(); foreach (control ctl in root.controls) { if (filtertype != null) { if (ctl.gettype == filtertype) { allchilds.add(ctl); } } else { allchilds.add(ctl); } if (ctl.haschildren) { getallchildcontrols(ctl, filtertype); } } return allchilds; } </code></pre>

<pre><code> ienumerable<control> ctrls = from control ctrl in me.controls where ctrl is textbox | ctrl is groupbox select ctr; </code></pre><p>лямбда-выражения</p><pre><code>ienumerable<control> ctrls = me.controls.cast<control>().where(c => c is button | c is groupbox); </code></pre>

<p>вот решение.</p><p> <a href="https://stackoverflow.com/a/19224936/1147352">https://stackoverflow.com/a/19224936/1147352</a> </p><p>я написал этот кусок кода и выбрал только панели, вы можете добавить больше переключателей или если. в этом</p>

<p>вот мой метод расширения для <code>control</code>, использующий linq, в качестве адаптации<strong> @psychocoder </strong>version:</p><p>вместо этого он принимает список типов, который позволяет вам не требовать нескольких вызовов <code>getall</code>, чтобы получить то, что вы хотите. я в настоящее время использую это как версию перегрузки.</p><pre><code>public static ienumerable<control> getall(this control control, ienumerable<type> filteringtypes) { var ctrls = control.controls.cast<control>(); return ctrls.selectmany(ctrl => getall(ctrl, filteringtypes)) .concat(ctrls) .where(ctl => filteringtypes.any(t => ctl.gettype() == t)); } </code></pre><p>использование:</p><pre><code>// the types you want to select var typetobeselected = new list<type> { typeof(textbox) , typeof(maskedtextbox) , typeof(button) }; // only one call var allcontrols = mycontrolthatcontainsothercontrols.getall(typetobeselected); // do something with it foreach(var ctrl in allcontrols) { ctrl.enabled = true; } </code></pre>

<p>вот мой метод расширения. это очень эффективно и лениво.</p><p>использование:</p><pre><code>var checkboxes = tablelayoutpanel1.findchildcontrolsoftype<checkbox>(); foreach (var checkbox in checkboxes) { checkbox.checked = false; } </code></pre><p>код является:</p><pre><code>public static ienumerable<tcontrol> findchildcontrolsoftype<tcontrol>(this control control) where tcontrol : control { foreach (var childcontrol in control.controls.cast<control>()) { if (childcontrol.gettype() == typeof(tcontrol)) { yield return (tcontrol)childcontrol; } else { foreach (var next in findchildcontrolsoftype<tcontrol>(childcontrol)) { yield return next; } } } } </code></pre>

<p>вот проверенное и работающее универсальное решение:</p><p>у меня есть большое количество элементов управления updownnumeric, некоторые в основной форме, некоторые в групповых полях внутри формы .... я хочу, чтобы только один последний выбранный элемент управления изменил задний цвет на зеленый, для чего я сначала установил все остальные на белый, используя этот метод: (также можно расширить до внуков)</p><pre><code> public void setallupdnbackcolorwhite() { //to set the numericupdown background color of the selected control to white: //and then the last selected control will change to green. foreach (control cont in this.controls) { if (cont.haschildren) { foreach (control contchild in cont.controls) if (contchild.gettype() == typeof(numericupdown)) contchild.backcolor = color.white; } if (cont.gettype() == typeof(numericupdown)) cont.backcolor = color.white; } } </code></pre>

<p>я изменил с @psychocoder . теперь можно найти все элементы управления (включая вложенные).</p><pre><code>public static ienumerable<t> getchildrens<t>(control control) { var type = typeof (t); var allcontrols = getallchildrens(control); return allcontrols.where(c => c.gettype() == type).cast<t>(); } private static ienumerable<control> getallchildrens(control control) { var controls = control.controls.cast<control>(); return controls.selectmany(c => getallchildrens(c)) .concat(controls); } </code></pre>

<p>вы можете попробовать это, если хотите :)</p><pre><code> private void clearcontrols(control.controlcollection c) { foreach (control control in c) { if (control.haschildren) { clearcontrols(control.controls); } else { if (control is textbox) { textbox txt = (textbox)control; txt.clear(); } if (control is combobox) { combobox cmb = (combobox)control; if (cmb.items.count > 0) cmb.selectedindex = -1; } if (control is checkbox) { checkbox chk = (checkbox)control; chk.checked = false; } if (control is radiobutton) { radiobutton rdo = (radiobutton)control; rdo.checked = false; } if (control is listbox) { listbox listbox = (listbox)control; listbox.clearselected(); } } } } private void btnclear\_click(object sender, eventargs e) { clearcontrols((controlcollection)this.controls); } </code></pre>

<p>это может работать:</p><pre><code>public function getcontrols(of t)() as list(of t) dim st as new stack(of control) dim ctl as control dim li as new list(of t) st.push(me) while st.count > 0 ctl = st.pop for each c in ctl.controls st.push(ctype(c, control)) if c.gettype is gettype(t) then li.add(ctype(c, t)) end if next end while return li end function </code></pre><p>я думаю, что функция получения всех элементов управления, о которых вы говорите, доступна только для <a href="http://en.wikipedia.org/wiki/windows\_presentation\_foundation">wpf</a> .</p>

<pre><code> public ienumerable<t> getall<t>(control control) where t : control { var type = typeof(t); var controls = control.controls.cast<control>().toarray(); foreach (var c in controls.selectmany(getall<t>).concat(controls)) if (c.gettype() == type) yield return (t)c; } </code></pre>

<p>для тех, кто ищет vb версию кода адама c #, написанного как расширение класса <code>control</code>:</p><pre><code>''' <summary>collects child controls of the specified type or base type within the passed control.</summary> ''' <typeparam name="t">the type of child controls to include. restricted to objects of type control.</typeparam> ''' <param name="parent">required. the parent form control.</param> ''' <returns>an object of type ienumerable(of t) containing the control collection.</returns> ''' <remarks>this method recursively calls itself passing child controls as the parent control.</remarks> <extension()> public function [getcontrols](of t as control)( byval parent as control) as ienumerable(of t) dim ocontrols as ienumerable(of control) = parent.controls.cast(of control)() return ocontrols.selectmany(function(c) getcontrols(of t)(c)).concat(ocontrols.where(function(c) c.gettype() is gettype(t) or c.gettype().basetype is gettype(t)) end function </code></pre><p>примечание. я добавил соответствие <code>basetype</code> для любых производных пользовательских элементов управления. вы можете удалить это или даже сделать его необязательным параметром, если хотите.</p><p>использование</p><pre><code>dim obuttons as ienumerable(of button) = me.getcontrols(of button)() </code></pre>

<p>хотя несколько других пользователей опубликовали адекватные решения, я хотел бы опубликовать более общий подход, который может быть более полезным. </p><p>это в значительной степени основано на ответе джелтона.</p><pre><code>public static ienumerable<control> allcontrols( this control control, func<control, boolean> filter = null) { if (control == null) throw new argumentnullexception("control"); if (filter == null) filter = (c => true); var list = new list<control>(); foreach (control c in control.controls) { list.addrange(allcontrols(c, filter)); if (filter(c)) list.add(c); } return list; } </code></pre>

<pre><code> public static ienumerable<t> getallcontrols<t>(this control control) where t : control { foreach (control c in control.controls) { if (c is t) yield return (t)c; foreach (t c1 in c.getallcontrols<t>()) yield return c1; } } </code></pre>