私はウィンドウのすべてのコントロールをそのタイプで見つける方法を探しています、
たとえば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>();
@Mathias Lykkegaard Lorenzenの提案と使用に従って、@ Bryce Kahleの回答を採用しました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でそれぞれ言及されているように、GroupBoxes内のタブコントロールまたはグリッドはまだチェックされません。)(@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 "Button"オブジェクトも取得することに注意してください。Textblock.Textパラメータに書き込んで「TextBlock」オブジェクトを再初期化すると、Button.Contentパラメータを使用してボタンテキストを変更できなくなります。ボタンは、Textblock.Text書き込みアクションから(それに取得されたときから)書き込まれたテキストを永続的に表示します-
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"の検索によって収集されません。
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;
};
何らかの理由で、ここに投稿された回答のどれも、メインウィンドウの特定のコントロールに含まれる特定のタイプのすべてのコントロールを取得するのに役立ちませんでした。繰り返し処理するために、1つのメニューですべてのメニュー項目を見つける必要がありました。それらはすべてメニューの直接の子孫ではなかったので、上記のコードのいずれかを使用して、それらの最初のlileのみを収集することができました。この拡張メソッドは、ここまでずっと読み続ける人のための問題の私の解決策です。
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
使用法(これにより、ウィンドウ内のすべてのTextBoxが無効になります):
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
}
}
};