Saya mencari cara untuk menemukan semua kontrol di Window berdasarkan tipenya,
misalnya: temukan semua TextBoxes
, temukan semua kontrol yang mengimplementasikan antarmuka spesifik, dll.
Saya mencari cara untuk menemukan semua kontrol di Window berdasarkan tipenya,
misalnya: temukan semua TextBoxes
, temukan semua kontrol yang mengimplementasikan antarmuka spesifik, dll.
Jawaban:
Ini harus melakukan trik
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;
}
}
}
}
maka Anda menghitung kontrol seperti itu
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
this
sebelum DependencyObject
=>this DependencyObject depObj
Ini cara termudah:
IEnumerable<myType> collection = control.Children.OfType<myType>();
di mana kontrol adalah elemen root dari jendela.
<Grid Name="Anata_wa_yoru_o_shihai_suru_ai">here buttons</Grid>
dan kemudian saya bisa menggunakanAnata_wa_yoru_o_shihai_suru_ai.Children.OfType<myType>();
Saya mengadaptasi jawaban @Bryce Kahle untuk mengikuti saran dan penggunaan @Mathias Lykkegaard Lorenzen LogicalTreeHelper
.
Tampaknya bekerja dengan baik. ;)
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;
}
}
}
}
}
(Masih tidak akan memeriksa kontrol tab atau Grid di dalam GroupBoxes seperti yang disebutkan oleh @Benjamin Berry & @David R masing-masing.) (Juga mengikuti saran @ noonand & menghapus anak yang berlebihan! = Null)
Gunakan kelas helper VisualTreeHelper
atau LogicalTreeHelper
tergantung pada pohon yang Anda minati. Keduanya menyediakan metode untuk mendapatkan anak-anak elemen (meskipun sintaks sedikit berbeda). Saya sering menggunakan kelas-kelas ini untuk menemukan kemunculan pertama dari jenis tertentu, tetapi Anda dapat dengan mudah memodifikasinya untuk menemukan semua objek jenis itu:
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;
}
Saya menemukan bahwa garis,, yang VisualTreeHelper.GetChildrenCount(depObj);
digunakan dalam beberapa contoh di atas tidak mengembalikan jumlah bukan nol untuk GroupBox
es, khususnya, di mana GroupBox
mengandung a Grid
, dan Grid
mengandung elemen anak-anak. Saya percaya ini mungkin karena GroupBox
tidak boleh mengandung lebih dari satu anak, dan ini disimpan di Content
propertinya. Tidak ada GroupBox.Children
tipe properti. Saya yakin saya tidak melakukan ini dengan sangat efisien, tetapi saya memodifikasi contoh "FindVisualChildren" pertama dalam rantai ini sebagai berikut:
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;
}
}
}
}
Untuk mendapatkan daftar semua anak dari jenis tertentu, Anda dapat menggunakan:
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;
}
Perubahan kecil untuk rekursi sehingga Anda dapat misalnya menemukan kontrol tab anak dari kontrol tab.
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;
}
Berikut ini adalah versi ringkas lainnya, dengan sintaksis generik:
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;
}
}
Dan ini cara kerjanya ke atas
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);
}
}
}
}
Perhatikan bahwa menggunakan VisualTreeHelper hanya berfungsi pada kontrol yang berasal dari Visual atau Visual3D. Jika Anda juga perlu memeriksa elemen-elemen lain (misalnya TextBlock, FlowDocument dll.), Menggunakan VisualTreeHelper akan memberikan pengecualian.
Berikut adalah alternatif yang jatuh kembali ke pohon logis jika perlu:
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
Saya ingin menambahkan komentar tetapi saya memiliki kurang dari 50 poin sehingga saya hanya dapat "Jawab". Ketahuilah bahwa jika Anda menggunakan metode "VisualTreeHelper" untuk mengambil objek XAML "TextBlock" maka ia juga akan mengambil objek "Tombol" XAML. Jika Anda menginisialisasi ulang objek "TextBlock" dengan menulis ke parameter Textblock.Text maka Anda tidak lagi dapat mengubah teks Button menggunakan parameter Button.Content. Tombol akan secara permanen menampilkan teks yang ditulis kepadanya dari Textblock.Teks tulis tindakan (dari saat itu diambil -
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.
}
Untuk mengatasinya, Anda dapat mencoba menggunakan XAML "TextBox" dan menambahkan metode (atau Acara) untuk meniru Tombol XAMAL. XAML "TextBox" tidak dikumpulkan oleh pencarian untuk "TextBlock".
Versi saya untuk 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;
};
Untuk beberapa alasan, tidak ada jawaban yang diposting di sini membantu saya untuk mendapatkan semua kontrol dari tipe yang diberikan terkandung dalam kontrol yang diberikan di MainWindow saya. Saya perlu menemukan semua item menu dalam satu menu untuk mengulanginya. Mereka tidak semua keturunan langsung dari menu, jadi saya berhasil mengumpulkan hanya lilne pertama dari mereka menggunakan salah satu kode di atas. Metode ekstensi ini adalah solusi saya untuk masalah bagi siapa saja yang akan terus membaca sampai sini.
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);
}
}
}
}
Semoga ini bisa membantu.
The jawaban yang diterima mengembalikan unsur-unsur yang ditemukan kurang lebih unordered , dengan mengikuti cabang anak pertama sedalam mungkin, sementara menghasilkan unsur-unsur ditemukan di sepanjang jalan, sebelum backtracking dan mengulangi langkah-langkah untuk cabang-cabang pohon belum diurai.
Jika Anda membutuhkan elemen turunan dalam urutan menurun , di mana anak-anak langsung akan dihasilkan pertama, maka anak-anak mereka dan seterusnya, algoritma berikut akan bekerja:
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;
}
}
}
Elemen yang dihasilkan akan dipesan dari yang terdekat hingga yang terjauh. Ini akan berguna misalnya jika Anda mencari elemen anak terdekat dari beberapa jenis dan kondisi:
var foundElement = GetDescendants<StackPanel>(someElement)
.FirstOrDefault(o => o.SomeProperty == SomeState);
child
tidak terdefinisi.
@ Bryce, jawaban yang sangat bagus.
Versi 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
Penggunaan (ini menonaktifkan semua Kotak Teks di jendela):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
tb.IsEnabled = False
Next
Saya merasa lebih mudah tanpa Pembantu Pohon Visual:
foreach (UIElement element in MainWindow.Children) {
if (element is TextBox) {
if ((element as TextBox).Text != "")
{
//Do something
}
}
};