publicstaticIEnumerable<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){yieldreturn(T)child;}foreach(T childOfChild inFindVisualChildren<T>(child)){yieldreturn childOfChild;}}}}
maka Anda menghitung kontrol seperti itu
foreach(TextBlock tb inFindVisualChildren<TextBlock>(window)){// do something with tb here}
Catatan: Jika Anda mencoba membuatnya berfungsi dan menemukan bahwa Jendela Anda (misalnya) memiliki 0 anak visual, coba jalankan metode ini di pengendali event Loaded. Jika Anda menjalankannya di konstruktor (bahkan setelah InitializeComponent ()), anak-anak visual belum dimuat, dan itu tidak akan berfungsi.
Ryan Lundy
24
Beralih dari VisualTreeHelper ke LogicalTreeHelpers akan menyebabkan elemen yang tidak terlihat dimasukkan juga.
Mathias Lykkegaard Lorenzen
11
Bukankah kalimat "child! = Null && child is T" berlebihan? Seharusnya tidak hanya membaca "child is T"
siang hari dan
1
Saya akan mengubahnya menjadi metode perpanjangan dengan hanya memasukkan thissebelum DependencyObject=>this DependencyObject depObj
Johannes Wanzek
1
@JohannesWanzek Jangan lupa Anda juga perlu mengubah bit tempat Anda menyebutnya pada anak: foreach (ChildofChild.FindVisualChildren <T> ()) {bla bla bla}
apa maksudmu "elemen root"? Apa yang harus saya tulis agar terhubung dengan formulir jendela utama saya?
deadfish
Saya mengerti, dalam tampilan xaml saya harus menetapkan nama untuk grid <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>();
deadfish
68
Ini tidak menjawab pertanyaan yang diajukan. Ini hanya mengembalikan kontrol anak sedalam satu level.
Jim
21
Saya mengadaptasi jawaban @Bryce Kahle untuk mengikuti saran dan penggunaan @Mathias Lykkegaard Lorenzen LogicalTreeHelper .
Tampaknya bekerja dengan baik. ;)
publicstaticIEnumerable<T>FindLogicalChildren<T>(DependencyObject depObj )where T :DependencyObject{if( depObj !=null){foreach(object rawChild inLogicalTreeHelper.GetChildren( depObj )){if( rawChild isDependencyObject){DependencyObject child =(DependencyObject)rawChild;if( child is T ){yieldreturn(T)child;}foreach( T childOfChild inFindLogicalChildren<T>( child )){yieldreturn 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)
telah mencari beberapa saat bagaimana menghapus semua kotak teks saya, saya memiliki banyak tab dan ini adalah satu-satunya kode yang bekerja :) terima kasih
JohnChris
13
Gunakan kelas helper VisualTreeHelperatau LogicalTreeHelpertergantung 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:
publicstaticDependencyObjectFindInVisualTreeDown(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;}}}returnnull;}
+1 untuk penjelasan dan posting tetapi Bryce Kahle memposting fungsi yang sepenuhnya berfungsi Terima kasih
Andrija
Ini tidak memperbaiki masalah pertanyaan, dan juga jawaban dengan tipe generik jauh lebih jelas. Menggabungkannya dengan penggunaan VisualTreeHelper.GetChildrenCount (obj) akan memperbaiki masalah ini. Namun bermanfaat untuk dipertimbangkan sebagai opsi.
Vasil Popov
9
Saya menemukan bahwa garis,, yang VisualTreeHelper.GetChildrenCount(depObj);digunakan dalam beberapa contoh di atas tidak mengembalikan jumlah bukan nol untuk GroupBoxes, khususnya, di mana GroupBoxmengandung a Grid, dan Gridmengandung elemen anak-anak. Saya percaya ini mungkin karena GroupBoxtidak boleh mengandung lebih dari satu anak, dan ini disimpan di Contentpropertinya. Tidak ada GroupBox.Childrentipe properti. Saya yakin saya tidak melakukan ini dengan sangat efisien, tetapi saya memodifikasi contoh "FindVisualChildren" pertama dalam rantai ini sebagai berikut:
publicIEnumerable<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){yieldreturn(T)child;}if(child isGroupBox){GroupBox gb = child asGroupBox;Object gpchild = gb.Content;if(gpchild is T){yieldreturn(T)child;
child = gpchild as T;}}foreach(T childOfChild inFindVisualChildren<T>(child)){yieldreturn childOfChild;}}}}
Berikut ini adalah versi ringkas lainnya, dengan sintaksis generik:
publicstaticIEnumerable<T>FindLogicalChildren<T>(DependencyObject obj)where T :DependencyObject{if(obj !=null){if(obj is T)yieldreturn obj as T;foreach(DependencyObject child inLogicalTreeHelper.GetChildren(obj).OfType<DependencyObject>())foreach(T c inFindLogicalChildren<T>(child))yieldreturn c;}}
private T FindParent<T>(DependencyObject item,TypeStopAt)where T :class{if(item is T){return item as T;}else{DependencyObject _parent =VisualTreeHelper.GetParent(item);if(_parent ==null){returndefault(T);}else{Type _type = _parent.GetType();if(StopAt!=null){if((_type.IsSubclassOf(StopAt)==true)||(_type ==StopAt)){returnnull;}}if((_type.IsSubclassOf(typeof(T))==true)||(_type ==typeof(T))){return _parent as T;}else{returnFindParent<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:
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 inFindVisualChildren<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".
Itulah perbedaan antara pohon visual dan logis. Pohon visual berisi setiap kontrol (termasuk kontrol yang dibuat, yang didefinisikan dalam templat kontrol) sementara pohon logis hanya berisi kontrol aktual (tanpa yang ditentukan dalam templat). Ada visualisasi yang bagus dari konsep ini di sini: tautan
lauxjpn
1
Versi saya untuk C ++ / CLI
template <class T,class U >boolIsinst(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.
publicstaticvoidFindVisualChildren<T>(thisICollection<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 isDependencyObject){var child = rawChild asDependencyObject;FindVisualChildren<T>(children, child);}}}}
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:
publicstaticIEnumerable<T>GetVisualDescendants<T>(DependencyObject parent,bool applyTemplates =false)where T :DependencyObject{if(parent ==null||!(child isVisual|| child isVisual3D))yieldbreak;var descendants =newQueue<DependencyObject>();
descendants.Enqueue(parent);while(descendants.Count>0){var currentDescendant = descendants.Dequeue();if(applyTemplates)(currentDescendant asFrameworkElement)?.ApplyTemplate();for(var i =0; i <VisualTreeHelper.GetChildrenCount(currentDescendant); i++){var child =VisualTreeHelper.GetChild(currentDescendant, i);if(child isVisual|| child isVisual3D)
descendants.Enqueue(child);if(child is T foundObject)yieldreturn 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);
PublicSharedIteratorFunctionFindVisualChildren(Of T AsDependencyObject)(depObj AsDependencyObject)AsIEnumerable(Of T)If depObj IsNotNothingThenFor i AsInteger=0ToVisualTreeHelper.GetChildrenCount(depObj)-1Dim child AsDependencyObject=VisualTreeHelper.GetChild(depObj, i)If child IsNotNothingAndAlsoTypeOf child Is T ThenYieldDirectCast(child, T)EndIfForEach childOfChild As T InFindVisualChildren(Of T)(child)Yield childOfChild
NextNextEndIfEndFunction
Penggunaan (ini menonaktifkan semua Kotak Teks di jendela):
Jawaban:
Ini harus melakukan trik
maka Anda menghitung kontrol seperti itu
sumber
this
sebelumDependencyObject
=>this DependencyObject depObj
Ini cara termudah:
di mana kontrol adalah elemen root dari jendela.
sumber
<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. ;)
(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)
sumber
Gunakan kelas helper
VisualTreeHelper
atauLogicalTreeHelper
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:sumber
Saya menemukan bahwa garis,, yang
VisualTreeHelper.GetChildrenCount(depObj);
digunakan dalam beberapa contoh di atas tidak mengembalikan jumlah bukan nol untukGroupBox
es, khususnya, di manaGroupBox
mengandung aGrid
, danGrid
mengandung elemen anak-anak. Saya percaya ini mungkin karenaGroupBox
tidak boleh mengandung lebih dari satu anak, dan ini disimpan diContent
propertinya. Tidak adaGroupBox.Children
tipe properti. Saya yakin saya tidak melakukan ini dengan sangat efisien, tetapi saya memodifikasi contoh "FindVisualChildren" pertama dalam rantai ini sebagai berikut:sumber
Untuk mendapatkan daftar semua anak dari jenis tertentu, Anda dapat menggunakan:
sumber
Perubahan kecil untuk rekursi sehingga Anda dapat misalnya menemukan kontrol tab anak dari kontrol tab.
sumber
Berikut ini adalah versi ringkas lainnya, dengan sintaksis generik:
sumber
Dan ini cara kerjanya ke atas
sumber
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
sumber
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 -
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".
sumber
Versi saya untuk C ++ / CLI
sumber
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.
Semoga ini bisa membantu.
sumber
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:
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:
sumber
child
tidak terdefinisi.@ Bryce, jawaban yang sangat bagus.
Versi VB.NET:
Penggunaan (ini menonaktifkan semua Kotak Teks di jendela):
sumber
Saya merasa lebih mudah tanpa Pembantu Pohon Visual:
sumber