WPF 창에서 유형별로 모든 컨트롤 찾기
윈도우의 모든 컨트롤을 유형별로 찾을 수 있는 방법을 찾고 있습니다.
예: 모두 찾기TextBoxes
특정 인터페이스 등을 구현하는 모든 컨트롤을 찾습니다.
이렇게 하면 효과가 있습니다.
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) yield return (T)Enumerable.Empty<T>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject ithChild = VisualTreeHelper.GetChild(depObj, i);
if (ithChild == null) continue;
if (ithChild is T t) yield return t;
foreach (T childOfChild in FindVisualChildren<T>(ithChild)) yield return childOfChild;
}
}
그런 다음 당신은 그렇게 컨트롤 위에 열거합니다.
foreach (TextBlock tb in FindVisualChildren<TextBlock>(window))
{
// do something with tb here
}
가장 쉬운 방법은 다음과 같습니다.
IEnumerable<myType> collection = control.Children.OfType<myType>();
여기서 control은 창의 루트 요소입니다.
EDIT - 댓글에 지적된 바와 같이.이건 깊이가 한 단계밖에 안 됩니다.자세한 내용은 승인된 답변을 참조하십시오.
나는 @Bryce Kahle의 대답을 @Mathias Lyckegaard 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;
}
}
}
}
}
(각각 @Benjamin Berry & @David R에서 언급한 것처럼 GroupBox 내부의 탭 컨트롤이나 그리드는 여전히 확인하지 않습니다.) (또한 @benjaminand의 제안에 따라 중복된 아이를 제거했습니다!= 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);
위의 몇 가지 예에서 사용된 값은 0이 아닌 카운트를 반환하지 않습니다.GroupBox
특히, 어디에서.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;
}
}
}
}
다음은 제네릭 구문이 포함된 또 다른 컴팩트 버전입니다.
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 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;
}
그리고 이것이 위쪽으로 작동하는 방식입니다.
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);
}
}
}
}
Visual Tree Helper를 사용하면 Visual 또는 Visual 3D에서 파생된 컨트롤에서만 작동합니다.다른 요소(예: TextBlock, FlowDocument 등)도 검사해야 하는 경우 VisualTreeHelper를 사용하면 예외가 발생합니다.
다음은 필요한 경우 논리 트리로 되돌아가는 대안입니다.
http://www.hardcodet.net/2009/06/finding-elements-in-wpf-tree-both-ways
C++/CLI용 My 버전
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;
};
@브라이스, 정말 좋은 대답입니다.
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
사용(창에 있는 모든 TextBox를 비활성화함):
For Each tb As TextBox In FindVisualChildren(Of TextBox)(Me)
tb.IsEnabled = False
Next
이와 같은 사용 사례의 경우 라이브러리에 흐름 확장 방법을 추가할 수 있습니다.
public static List<DependencyObject> FindAllChildren(this DependencyObject dpo, Predicate<DependencyObject> predicate)
{
var results = new List<DependencyObject>();
if (predicate == null)
return results;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dpo); i++)
{
var child = VisualTreeHelper.GetChild(dpo, i);
if (predicate(child))
results.Add(child);
var subChildren = child.FindAllChildren(predicate);
results.AddRange(subChildren);
}
return results;
}
예를 들어 다음과 같습니다.
var children = dpObject.FindAllChildren(child => child is TextBox);
댓글을 추가하고 싶었는데 제가 50점 미만이라 '응답'만 가능합니다."VisualTreeHelper" 메서드를 사용하여 XAML "TextBlock" 개체를 검색하면 XAML "Button" 개체도 가져옵니다.텍스트 블록에 기록하여 "TextBlock" 개체를 다시 초기화하는 경우.텍스트 매개변수를 사용하면 더 이상 단추를 사용하여 단추 텍스트를 변경할 수 없습니다.내용 매개 변수입니다.단추는 텍스트 블록에서 작성된 텍스트를 영구적으로 표시합니다.텍스트 쓰기 작업(검색된 시점부터 -
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 "TextBox"를 사용하고 메서드(또는 이벤트)를 추가하여 XAMAL 단추를 모방할 수 있습니다.XAML "TextBox"가 "TextBlock" 검색에 의해 수집되지 않습니다.
어떤 이유에서인지 여기에 게시된 답변은 기본 창에 지정된 컨트롤에 포함된 특정 유형의 모든 컨트롤을 가져오는 데 도움이 되지 않았습니다.저는 모든 메뉴 항목을 한 메뉴에서 찾아 반복해야 했습니다.그들은 모두 메뉴의 직계 후손이 아니었기 때문에 위의 코드 중 하나를 사용하여 첫 번째 라일린만 수집할 수 있었습니다.이 확장 방법은 여기까지 계속해서 읽을 사람들을 위한 문제에 대한 저의 해결책입니다.
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);
}
}
}
}
도움이 되길 바랍니다.
Visual Tree Helper 없이도 더 쉽게 찾을 수 있었습니다.
foreach (UIElement element in MainWindow.Children) {
if (element is TextBox) {
if ((element as TextBox).Text != "")
{
//Do something
}
}
};
승인된 응답은 발견된 요소를 순서 없이 반환합니다. 첫 번째 자식 분기를 가능한 한 깊이 따라가면서 발견된 요소를 반환하는 동시에 아직 구문 분석되지 않은 트리 분기에 대한 단계를 역추적하고 반복합니다.
하위 요소가 내림차순으로 필요한 경우 다음 알고리즘이 작동합니다. 여기서 직접 하위 요소가 먼저 생성되고 하위 요소가 생성됩니다.
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);
언급URL : https://stackoverflow.com/questions/974598/find-all-controls-in-wpf-window-by-type
'programing' 카테고리의 다른 글
Apache POI의 행 자동 크기 높이 (0) | 2023.05.28 |
---|---|
"xcode-select-switch"를 설정한 후 Xcode가 작동을 중지함 (0) | 2023.05.28 |
목표-C에서 언제 0과 NULL을 사용해야 합니까? (0) | 2023.05.28 |
Makefile에서 셸 명령어를 사용하는 방법 (0) | 2023.05.28 |
Xcode 11에 Assistant Editor 누락 표시? (0) | 2023.05.28 |