ฉันกำลังมองหาวิธีที่จะหาการควบคุมทั้งหมดบนหน้าต่างตามประเภทของพวกเขา
ตัวอย่างเช่น:ค้นหาทั้งหมดTextBoxes
, ค้นหาตัวควบคุมทั้งหมดที่ใช้อินเทอร์เฟซเฉพาะเป็นต้น
ฉันกำลังมองหาวิธีที่จะหาการควบคุมทั้งหมดบนหน้าต่างตามประเภทของพวกเขา
ตัวอย่างเช่น:ค้นหาทั้งหมดTextBoxes
, ค้นหาตัวควบคุมทั้งหมดที่ใช้อินเทอร์เฟซเฉพาะเป็นต้น
คำตอบ:
สิ่งนี้ควรทำเคล็ดลับ
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
จากนั้นคุณระบุการควบคุมเช่นนั้น
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
this
ก่อนDependencyObject
=>this DependencyObject depObj
นี่เป็นวิธีที่ง่ายที่สุด:
IEnumerable<myType> collection = control.Children.OfType<myType>();
โดยที่ control เป็นองค์ประกอบรูทของหน้าต่าง
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
แล้วฉันสามารถใช้Anata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
ผมดัดแปลงคำตอบ @Bryce Kahle ที่จะปฏิบัติตามข้อเสนอแนะ @Mathias Lykkegaard Lorenzen LogicalTreeHelper
และการใช้งาน
ดูเหมือนว่าจะทำงานได้ดี ;)
public static IEnumerable<T> FindLogicalChildren<T> ( DependencyObject depObj ) where T : DependencyObject
{
if( depObj != null )
{
foreach( object rawChild in LogicalTreeHelper.GetChildren( depObj ) )
{
if( rawChild is DependencyObject )
{
DependencyObject child = (DependencyObject)rawChild;
if( child is T )
{
yield return (T)child;
}
foreach( T childOfChild in FindLogicalChildren<T>( child ) )
{
yield return childOfChild;
}
}
}
}
}
(มันจะไม่ตรวจสอบการควบคุมแท็บหรือกริดภายใน GroupBox ตามที่กล่าวถึงโดย @Benjamin Berry & @David R ตามลำดับ) (ตามด้วยคำแนะนำของ @ noonand & ลบเด็กที่ซ้ำซ้อน! = null)
ใช้คลาสตัวช่วยVisualTreeHelper
หรือLogicalTreeHelper
ขึ้นอยู่กับต้นไม้ที่คุณสนใจทั้งคู่มีวิธีการรับลูกองค์ประกอบ (แม้ว่าไวยากรณ์จะแตกต่างกันเล็กน้อย) ฉันมักจะใช้คลาสเหล่านี้เพื่อค้นหาการเกิดขึ้นครั้งแรกของประเภทเฉพาะ แต่คุณสามารถปรับเปลี่ยนได้อย่างง่ายดายเพื่อค้นหาวัตถุประเภทนั้นทั้งหมด:
public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type)
{
if (obj != null)
{
if (obj.GetType() == type)
{
return obj;
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject childReturn = FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type);
if (childReturn != null)
{
return childReturn;
}
}
}
return null;
}
ฉันพบว่าบรรทัดที่VisualTreeHelper.GetChildrenCount(depObj);
ใช้ในตัวอย่างด้านบนไม่ได้ส่งกลับจำนวนที่ไม่เป็นศูนย์สำหรับGroupBox
es โดยเฉพาะอย่างยิ่งที่GroupBox
มีGrid
และและGrid
องค์ประกอบที่มีเด็ก ฉันเชื่อว่าอาจเป็นเพราะGroupBox
ไม่ได้รับอนุญาตให้มีเด็กมากกว่าหนึ่งคนและสิ่งนี้ถูกเก็บไว้ในContent
ทรัพย์สินของมัน ไม่มีGroupBox.Children
ประเภทของทรัพย์สิน ฉันแน่ใจว่าฉันไม่ได้ทำอย่างมีประสิทธิภาพมาก แต่ฉันแก้ไขตัวอย่าง "FindVisualChildren" แรกในกลุ่มนี้ดังนี้:
public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
int depObjCount = VisualTreeHelper.GetChildrenCount(depObj);
for (int i = 0; i <depObjCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
if (child is GroupBox)
{
GroupBox gb = child as GroupBox;
Object gpchild = gb.Content;
if (gpchild is T)
{
yield return (T)child;
child = gpchild as T;
}
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
ในการรับรายการประเภทลูกทั้งหมดคุณสามารถใช้:
private static IEnumerable<DependencyObject> FindInVisualTreeDown(DependencyObject obj, Type type)
{
if (obj != null)
{
if (obj.GetType() == type)
{
yield return obj;
}
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type))
{
if (child != null)
{
yield return child;
}
}
}
}
yield break;
}
เปลี่ยนการเรียกซ้ำเป็นเล็กน้อยเพื่อให้คุณสามารถค้นหาการควบคุมแท็บย่อยของการควบคุมแท็บได้
public static DependencyObject FindInVisualTreeDown(DependencyObject obj, Type type)
{
if (obj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child.GetType() == type)
{
return child;
}
DependencyObject childReturn = FindInVisualTreeDown(child, type);
if (childReturn != null)
{
return childReturn;
}
}
}
return null;
}
นี่คืออีกเวอร์ชั่นกะทัดรัดที่มีไวยากรณ์ทั่วไป:
public static IEnumerable<T> FindLogicalChildren<T>(DependencyObject obj) where T : DependencyObject
{
if (obj != null) {
if (obj is T)
yield return obj as T;
foreach (DependencyObject child in LogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>())
foreach (T c in FindLogicalChildren<T>(child))
yield return c;
}
}
และนี่คือวิธีการทำงาน
private T FindParent<T>(DependencyObject item, Type StopAt) where T : class
{
if (item is T)
{
return item as T;
}
else
{
DependencyObject _parent = VisualTreeHelper.GetParent(item);
if (_parent == null)
{
return default(T);
}
else
{
Type _type = _parent.GetType();
if (StopAt != null)
{
if ((_type.IsSubclassOf(StopAt) == true) || (_type == StopAt))
{
return null;
}
}
if ((_type.IsSubclassOf(typeof(T)) == true) || (_type == typeof(T)))
{
return _parent as T;
}
else
{
return FindParent<T>(_parent, StopAt);
}
}
}
}
โปรดทราบว่าการใช้ VisualTreeHelper ใช้งานได้เฉพาะกับส่วนควบคุมที่ได้รับจาก Visual หรือ Visual3D เท่านั้น หากคุณต้องการตรวจสอบองค์ประกอบอื่น ๆ (เช่น TextBlock, FlowDocument เป็นต้น) การใช้ VisualTreeHelper จะทำให้เกิดข้อยกเว้น
นี่เป็นอีกทางเลือกหนึ่งที่จะกลับไปที่แผนผังโลจิคัลหากจำเป็น
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
ฉันต้องการเพิ่มความคิดเห็น แต่ฉันมีน้อยกว่า 50 แต้มดังนั้นฉันจึงสามารถ "ตอบ" ได้เท่านั้น โปรดระวังว่าถ้าคุณใช้วิธี "VisualTreeHelper" เพื่อดึงวัตถุ XAML "TextBlock" จากนั้นก็จะดึงวัตถุ XAML "ปุ่ม" หากคุณเริ่มต้นวัตถุ "TextBlock" อีกครั้งโดยการเขียนพารามิเตอร์ Textblock.Text คุณจะไม่สามารถเปลี่ยนข้อความของปุ่มโดยใช้พารามิเตอร์ Button.Content ปุ่มจะแสดงข้อความที่ถูกเขียนอย่างถาวรจาก Textblock.Text write action (จากเมื่อถูกดึงมา -
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
tb.Text = ""; //this will overwrite Button.Content and render the
//Button.Content{set} permanently disabled.
}
ในการแก้ไขปัญหานี้คุณสามารถลองใช้ XAML "กล่องข้อความ" และเพิ่มวิธีการ (หรือเหตุการณ์) เพื่อเลียนแบบปุ่ม XAMAL XAML "กล่องข้อความ" ไม่ได้ถูกรวบรวมโดยการค้นหา "TextBlock"
รุ่นของฉันสำหรับ C ++ / CLI
template < class T, class U >
bool Isinst(U u)
{
return dynamic_cast< T >(u) != nullptr;
}
template <typename T>
T FindVisualChildByType(Windows::UI::Xaml::DependencyObject^ element, Platform::String^ name)
{
if (Isinst<T>(element) && dynamic_cast<Windows::UI::Xaml::FrameworkElement^>(element)->Name == name)
{
return dynamic_cast<T>(element);
}
int childcount = Windows::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(element);
for (int i = 0; i < childcount; ++i)
{
auto childElement = FindVisualChildByType<T>(Windows::UI::Xaml::Media::VisualTreeHelper::GetChild(element, i), name);
if (childElement != nullptr)
{
return childElement;
}
}
return nullptr;
};
ด้วยเหตุผลบางอย่างไม่มีคำตอบที่โพสต์ที่นี่ช่วยให้ฉันได้รับการควบคุมทั้งหมดของประเภทที่กำหนดอยู่ในการควบคุมที่กำหนดใน MainWindow ของฉัน ฉันต้องการค้นหารายการเมนูทั้งหมดในเมนูเดียวเพื่อทำซ้ำ พวกเขาไม่ใช่ทายาทโดยตรงของเมนูดังนั้นฉันจัดการเพื่อรวบรวมเฉพาะ lilne แรกของพวกเขาโดยใช้รหัสใด ๆ ข้างต้น วิธีการขยายนี้เป็นวิธีการแก้ปัญหาของฉันสำหรับทุกคนที่จะอ่านต่อไปจนถึงที่นี่
public static void FindVisualChildren<T>(this ICollection<T> children, DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
var brethren = LogicalTreeHelper.GetChildren(depObj);
var brethrenOfType = LogicalTreeHelper.GetChildren(depObj).OfType<T>();
foreach (var childOfType in brethrenOfType)
{
children.Add(childOfType);
}
foreach (var rawChild in brethren)
{
if (rawChild is DependencyObject)
{
var child = rawChild as DependencyObject;
FindVisualChildren<T>(children, child);
}
}
}
}
หวังว่ามันจะช่วย
คำตอบที่ได้รับการยอมรับผลตอบแทนองค์ประกอบพบว่ามากหรือน้อยไม่เรียงลำดับโดยต่อไปนี้สาขาลูกคนแรกให้ลึกที่สุดเท่าที่เป็นไปได้ในขณะที่ผลผลิตองค์ประกอบที่ค้นพบไปพร้อมกันก่อนที่จะย้อนรอยและการทำซ้ำขั้นตอนสำหรับการที่ยังไม่ได้แยกวิเคราะห์กิ่งไม้
หากคุณต้องการองค์ประกอบเรียงลำดับจากมากไปน้อยซึ่งจะให้ผลโดยตรงกับลูกก่อนจากนั้นลูกของพวกเขาและอื่น ๆ อัลกอริทึมต่อไปนี้จะทำงาน:
public static IEnumerable<T> GetVisualDescendants<T>(DependencyObject parent, bool applyTemplates = false)
where T : DependencyObject
{
if (parent == null || !(child is Visual || child is Visual3D))
yield break;
var descendants = new Queue<DependencyObject>();
descendants.Enqueue(parent);
while (descendants.Count > 0)
{
var currentDescendant = descendants.Dequeue();
if (applyTemplates)
(currentDescendant as FrameworkElement)?.ApplyTemplate();
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(currentDescendant); i++)
{
var child = VisualTreeHelper.GetChild(currentDescendant, i);
if (child is Visual || child is Visual3D)
descendants.Enqueue(child);
if (child is T foundObject)
yield return foundObject;
}
}
}
องค์ประกอบที่เป็นผลลัพธ์จะถูกจัดเรียงจากใกล้ที่สุดไปหาไกลที่สุด สิ่งนี้จะเป็นประโยชน์เช่นหากคุณกำลังมองหาองค์ประกอบย่อยที่ใกล้ที่สุดของบางประเภทและเงื่อนไข:
var foundElement = GetDescendants<StackPanel>(someElement)
.FirstOrDefault(o => o.SomeProperty == SomeState);
child
ไม่ได้กำหนด
@ ไบรซ์คำตอบที่ดีจริงๆ
รุ่น VB.NET:
Public Shared Iterator Function FindVisualChildren(Of T As DependencyObject)(depObj As DependencyObject) As IEnumerable(Of T)
If depObj IsNot Nothing Then
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(depObj) - 1
Dim child As DependencyObject = VisualTreeHelper.GetChild(depObj, i)
If child IsNot Nothing AndAlso TypeOf child Is T Then
Yield DirectCast(child, T)
End If
For Each childOfChild As T In FindVisualChildren(Of T)(child)
Yield childOfChild
Next
Next
End If
End Function
การใช้งาน (สิ่งนี้ปิดการใช้งานกล่องข้อความทั้งหมดในหน้าต่าง):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
tb.IsEnabled = False
Next
ฉันพบว่าง่ายขึ้นโดยไม่มี Visual Tree Helpers:
foreach (UIElement element in MainWindow.Children) {
if (element is TextBox) {
if ((element as TextBox).Text != "")
{
//Do something
}
}
};