Yanıtlar:
Herhangi bir ebeveyn üzerinde kullanılabilecek bir findChild Algoritması oluşturmak için yukarıdaki John Myczek ve Tri Q algoritması tarafından kullanılan şablon formatını birleştirdim. Bir ağacı tekrar tekrar aşağı doğru aramanın uzun bir süreç olabileceğini unutmayın. Ben sadece bir WPF uygulamasında bu nokta kontrol ettik, lütfen bulabileceğiniz herhangi bir hata hakkında yorum ve kodumu düzeltirim.
WPF Snoop görsel ağaca bakarken faydalı bir araçtır - işinizi kontrol etmek için test ederken veya bu algoritmayı kullanırken şiddetle tavsiye ederim.
Tri Q Algoritmasında küçük bir hata var. Çocuk bulunduktan sonra childrenCount değeri> 1 ise ve tekrar edersek, düzgün bulunan çocuğun üzerine yazabiliriz. Bu nedenle if (foundChild != null) break;
bu koşulla başa çıkmak için koduma bir ekledim .
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
Buna şöyle deyin:
TextBox foundTextBox =
UIHelper.FindChild<TextBox>(Application.Current.MainWindow, "myTextBoxName");
Not Application.Current.MainWindow
herhangi bir üst pencere olabilir.
FrameworkElement
T olarak iletirseniz, ilk döngü biter bitmez null değerini döndürür. bu yüzden bazı değişiklikler yapmanız gerekecek.
FrameworkElement.FindName (string) öğesini kullanarak bir öğeyi ada göre de bulabilirsiniz .
Verilen:
<UserControl ...>
<TextBlock x:Name="myTextBlock" />
</UserControl>
Arkasındaki kod dosyasına şunları yazabilirsiniz:
var myTextBlock = (TextBlock)this.FindName("myTextBlock");
Elbette, x: Name kullanılarak tanımlandığından, oluşturulan alana referans verebilirsiniz, ancak belki de statik olarak değil dinamik olarak bakmak istersiniz.
Bu yaklaşım, adlandırılan öğenin birden çok kez göründüğü şablonlar için de kullanılabilir (şablonun kullanımı başına bir kez).
Denetimleri bulmak için VisualTreeHelper kullanabilirsiniz . Aşağıda, belirtilen türde bir üst denetim bulmak için VisualTreeHelper kullanan bir yöntem bulunmaktadır. Denetimleri başka şekillerde de bulmak için VisualTreeHelper kullanabilirsiniz.
public static class UIHelper
{
/// <summary>
/// Finds a parent of a given item on the visual tree.
/// </summary>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="child">A direct or indirect child of the queried item.</param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found, a null reference is being returned.</returns>
public static T FindVisualParent<T>(DependencyObject child)
where T : DependencyObject
{
// get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
// we’ve reached the end of the tree
if (parentObject == null) return null;
// check if the parent matches the type we’re looking for
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
// use recursion to proceed with next level
return FindVisualParent<T>(parentObject);
}
}
}
Buna şöyle deyin:
Window owner = UIHelper.FindVisualParent<Window>(myControl);
Ben sadece herkesi tekrar ediyor olabilirim ama size tip ve ada göre çocuğu alacak bir yöntem FindChild () ile DependencyObject sınıfını genişleten güzel bir kod parçası var. Sadece dahil edin ve kullanın.
public static class UIChildFinder
{
public static DependencyObject FindChild(this DependencyObject reference, string childName, Type childType)
{
DependencyObject foundChild = null;
if (reference != null)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(reference);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(reference, i);
// If the child is not of the request child type child
if (child.GetType() != childType)
{
// recursively drill down the tree
foundChild = FindChild(child, childName, childType);
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = child;
break;
}
}
else
{
// child element found.
foundChild = child;
break;
}
}
}
return foundChild;
}
}
Umarım faydalı bulursunuz.
Koda uzantılarım.
Kaynak: https://code.google.com/p/gishu-util/source/browse/#git%2FWPF%2FUtilities
Açıklayıcı blog yazısı: http://madcoderspeak.blogspot.com/2010/04/wpf-find-child-control-of-specific-type.html
Belirli bir türdeki TÜM denetimleri bulmak istiyorsanız, bu snippet ile de ilgilenebilirsiniz
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject parent)
where T : DependencyObject
{
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
var childType = child as T;
if (childType != null)
{
yield return (T)child;
}
foreach (var other in FindVisualChildren<T>(child))
{
yield return other;
}
}
}
child
ikinci kez oynuyorsun ? Eğer childType
türünüz T
varsa, içine yazabilirsiniz if
: yield return childType
... hayır?
Bu, bazı öğeleri kapatacaktır - daha geniş bir kontrol dizisini desteklemek için bu şekilde genişletmelisiniz. Kısa bir tartışma için buraya bir göz atın
/// <summary>
/// Helper methods for UI-related tasks.
/// </summary>
public static class UIHelper
{
/// <summary>
/// Finds a parent of a given item on the visual tree.
/// </summary>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="child">A direct or indirect child of the
/// queried item.</param>
/// <returns>The first parent item that matches the submitted
/// type parameter. If not matching item can be found, a null
/// reference is being returned.</returns>
public static T TryFindParent<T>(DependencyObject child)
where T : DependencyObject
{
//get parent item
DependencyObject parentObject = GetParentObject(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
//use recursion to proceed with next level
return TryFindParent<T>(parentObject);
}
}
/// <summary>
/// This method is an alternative to WPF's
/// <see cref="VisualTreeHelper.GetParent"/> method, which also
/// supports content elements. Do note, that for content element,
/// this method falls back to the logical tree of the element!
/// </summary>
/// <param name="child">The item to be processed.</param>
/// <returns>The submitted item's parent, if available. Otherwise
/// null.</returns>
public static DependencyObject GetParentObject(DependencyObject child)
{
if (child == null) return null;
ContentElement contentElement = child as ContentElement;
if (contentElement != null)
{
DependencyObject parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
FrameworkContentElement fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
//if it's not a ContentElement, rely on VisualTreeHelper
return VisualTreeHelper.GetParent(child);
}
}
Try*
yöntemin dönmesini bool
ve out
söz konusu türü döndüren bir parametreye sahip olmasını bool IDictionary.TryGetValue(TKey key, out TValue value)
FindParent
. Bu isim bana geri dönebileceğini ima ediyor null
. Ön Try*
ek, yukarıda açıkladığım şekilde BCL boyunca kullanılır. Ayrıca, buradaki diğer yanıtların çoğunun Find*
adlandırma kuralını kullandığını unutmayın . Gerçi sadece küçük bir nokta :)
Süper sınıf türleriyle çalışmadığı için CrimsonX kodunu düzenledim:
public static T FindChild<T>(DependencyObject depObj, string childName)
where T : DependencyObject
{
// Confirm obj is valid.
if (depObj == null) return null;
// success case
if (depObj is T && ((FrameworkElement)depObj).Name == childName)
return depObj as T;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
//DFS
T obj = FindChild<T>(child, childName);
if (obj != null)
return obj;
}
return null;
}
DependencyObject
olmayan bir yöntemden geçerseniz , FrameworkElement
bir İstisna atabilir. Ayrıca döngünün GetChildrenCount
her yinelemesinde kullanmak for
kötü bir fikir gibi geliyor.
Genel olarak özyinelemeyi sevmeme rağmen, C #'da programlama yaparken yineleme kadar verimli değil, bu yüzden belki de aşağıdaki çözüm John Myczek tarafından önerilenlerden daha temiz mi? Bu, belirli bir türün üst denetimini bulmak için belirli bir denetimden bir hiyerarşi arar.
public static T FindVisualAncestorOfType<T>(this DependencyObject Elt)
where T : DependencyObject
{
for (DependencyObject parent = VisualTreeHelper.GetParent(Elt);
parent != null; parent = VisualTreeHelper.GetParent(parent))
{
T result = parent as T;
if (result != null)
return result;
}
return null;
}
Şu Window
adlı bir denetimi bulmak için buna ExampleTextBox
şöyle deyin :
Window window = ExampleTextBox.FindVisualAncestorOfType<Window>();
İşte, hiyerarşiye ne kadar derine girdiğimizi kontrol ederken Type'a göre kontrolleri bulmak için kodum (maxDepth == 0 sonsuz derin anlamına gelir).
public static class FrameworkElementExtension
{
public static object[] FindControls(
this FrameworkElement f, Type childType, int maxDepth)
{
return RecursiveFindControls(f, childType, 1, maxDepth);
}
private static object[] RecursiveFindControls(
object o, Type childType, int depth, int maxDepth = 0)
{
List<object> list = new List<object>();
var attrs = o.GetType()
.GetCustomAttributes(typeof(ContentPropertyAttribute), true);
if (attrs != null && attrs.Length > 0)
{
string childrenProperty = (attrs[0] as ContentPropertyAttribute).Name;
foreach (var c in (IEnumerable)o.GetType()
.GetProperty(childrenProperty).GetValue(o, null))
{
if (c.GetType().FullName == childType.FullName)
list.Add(c);
if (maxDepth == 0 || depth < maxDepth)
list.AddRange(RecursiveFindControls(
c, childType, depth + 1, maxDepth));
}
}
return list.ToArray();
}
}
exciton80 ... Ben usercontrols üzerinden yinelenen kod ile ilgili bir sorun yaşıyordu. Izgara köküne vuruyor ve bir hata atıyordu. Bunun benim için düzelttiğine inanıyorum:
public static object[] FindControls(this FrameworkElement f, Type childType, int maxDepth)
{
return RecursiveFindControls(f, childType, 1, maxDepth);
}
private static object[] RecursiveFindControls(object o, Type childType, int depth, int maxDepth = 0)
{
List<object> list = new List<object>();
var attrs = o.GetType().GetCustomAttributes(typeof(ContentPropertyAttribute), true);
if (attrs != null && attrs.Length > 0)
{
string childrenProperty = (attrs[0] as ContentPropertyAttribute).Name;
if (String.Equals(childrenProperty, "Content") || String.Equals(childrenProperty, "Children"))
{
var collection = o.GetType().GetProperty(childrenProperty).GetValue(o, null);
if (collection is System.Windows.Controls.UIElementCollection) // snelson 6/6/11
{
foreach (var c in (IEnumerable)collection)
{
if (c.GetType().FullName == childType.FullName)
list.Add(c);
if (maxDepth == 0 || depth < maxDepth)
list.AddRange(RecursiveFindControls(
c, childType, depth + 1, maxDepth));
}
}
else if (collection != null && collection.GetType().BaseType.Name == "Panel") // snelson 6/6/11; added because was skipping control (e.g., System.Windows.Controls.Grid)
{
if (maxDepth == 0 || depth < maxDepth)
list.AddRange(RecursiveFindControls(
collection, childType, depth + 1, maxDepth));
}
}
}
return list.ToArray();
}
(Tamamen genel) böyle bir dizi işlevi var:
public static IEnumerable<T> SelectAllRecursively<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> func)
{
return (items ?? Enumerable.Empty<T>()).SelectMany(o => new[] { o }.Concat(SelectAllRecursively(func(o), func)));
}
Acil çocuk sahibi olma:
public static IEnumerable<DependencyObject> FindChildren(this DependencyObject obj)
{
return Enumerable.Range(0, VisualTreeHelper.GetChildrenCount(obj))
.Select(i => VisualTreeHelper.GetChild(obj, i));
}
Hiyararşik ağacın altındaki tüm çocukları bulmak:
public static IEnumerable<DependencyObject> FindAllChildren(this DependencyObject obj)
{
return obj.FindChildren().SelectAllRecursively(o => o.FindChildren());
}
Tüm kontrolleri almak için bunu Pencereden çağırabilirsiniz.
Koleksiyona sahip olduktan sonra, LINQ (yani OfType, Where) kullanabilirsiniz.
Soru, çok önemsiz vakalara cevap arayan insanları çekebilecek kadar genel olduğundan: soyundan ziyade sadece bir çocuk istiyorsanız, Linq'i kullanabilirsiniz:
private void ItemsControlItem_Loaded(object sender, RoutedEventArgs e)
{
if (SomeCondition())
{
var children = (sender as Panel).Children;
var child = (from Control child in children
where child.Name == "NameTextBox"
select child).First();
child.Focus();
}
}
veya elbette Çocuklar üzerinde döngü yinelemeleri için aşikâr.
Bu seçenekler zaten C # 'da Görsel Ağacın üzerinden geçmekten bahsediyor. RelativeSource işaretleme uzantısını kullanarak xaml'de görsel ağacı da dolaşmak mümkündür. msdn
türe göre bul
Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type <TypeToFind>}}}"
İşte esnek yüklem kullanan bir çözüm:
public static DependencyObject FindChild(DependencyObject parent, Func<DependencyObject, bool> predicate)
{
if (parent == null) return null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (predicate(child))
{
return child;
}
else
{
var foundChild = FindChild(child, predicate);
if (foundChild != null)
return foundChild;
}
}
return null;
}
Örneğin şöyle diyebilirsiniz:
var child = FindChild(parent, child =>
{
var textBlock = child as TextBlock;
if (textBlock != null && textBlock.Name == "MyTextBlock")
return true;
else
return false;
}) as TextBlock;
Bu kod sadece @CrimsonX yanıtlayıcının hatasını giderir:
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
Türler eşleşiyor, ancak adlar eşleşmiyorsa yöntemi özyinelemeli olarak çağırmaya devam etmeniz gerekir (bu, FrameworkElement
olarak geçtiğinizde olur T
). aksi halde geri dönecek null
ve bu yanlış.
Koddan belirli bir türde bir ata bulmak için şunları kullanabilirsiniz:
[CanBeNull]
public static T FindAncestor<T>(DependencyObject d) where T : DependencyObject
{
while (true)
{
d = VisualTreeHelper.GetParent(d);
if (d == null)
return null;
var t = d as T;
if (t != null)
return t;
}
}
Bu uygulama, biraz daha hızlı olabilen özyineleme yerine yineleme kullanır.
C # 7 kullanıyorsanız, bu biraz daha kısa yapılabilir:
[CanBeNull]
public static T FindAncestor<T>(DependencyObject d) where T : DependencyObject
{
while (true)
{
d = VisualTreeHelper.GetParent(d);
if (d == null)
return null;
if (d is T t)
return t;
}
}