Как вы упомянули, ведение списка подвержено ошибкам - вам нужно будет синхронизировать его с визуальным деревом WPF. Вместо того, чтобы поддерживать список, вы можете пройти по Визуальному Дереву из заданного корня. В вашем случае это звучит как корень Panel
.
Обход дерева визуалов довольно прост. Этот пост показывает несколько примеров. То, что я считаю уместным, это:
public static IEnumerable<DependencyObject> GetVisualTree(this DependencyObject element)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < childrenCount; i++)
{
var visualChild = VisualTreeHelper.GetChild(element, i);
yield return visualChild;
foreach (var visualChildren in GetVisualTree(visualChild))
{
yield return visualChildren;
}
}
}
Это делает глубинный обход всех визуальных потомков данного DependencyObject
. Таким образом, для любого данного Panel
вы можете вызвать этот метод. Поскольку это IEnumerable
, вы можете использовать LINQ для фильтрации результатов в свой пользовательский UserControl.
Для вашего другого требования («Любой другой элемент управления, который также является дочерним элементом этой панели, должен иметь возможность перечислять кнопки клавиатуры своего родителя»): Если у вас есть дочерний элемент Panel
, пройдите дерево визуалов вверх, чтобы найти первый Panel
и используйте тот же процесс, чтобы получить всех соответствующих детей.
Если вам действительно нужно это как DependencyProperty
(например, для привязки данных), я думаю, вы могли бы сделать его доступным только для чтения и все еще использовать метод визуального обхода дерева в получателе, но я не уверен.