Разработчики WPF (и Silverlight) меня поймут. Microsoft создала мощную технологию для создания пользовательского интерфейса декларативным образом. Но, к сожалению, не все можно реализовать декларативно. Приходится иногда и код месить. И вот тут ожидает неприятный сюрприз. Концептуально понятная иерархия интерфейсных элементов оказывается недоступной напрямую, а чтобы достучаться до неё приходится обращаться к классу VisualTreeHelper. Это, бесспорно, очень нужный и полезный класс, но использование методов типа GetChildrenCount() и GetChild() в эпоху LINQ несколько раздражает.
Я уже писал о пользе и удобстве расширений, когда рассказывал о расширениях для класса Exception, повторяться не буду. А расскажу о нескольких очень простых расширениях для класса FrameworkElement, которые очень облегчают жизнь в некоторых сценариях.
Хочу обратить внимание, что перечисление – это Linq выражение, которое вычисляется лениво, т.е. если выполняется перебор не всех элементов, то лишних обращений к методам VisualTreeHelper не происходит.
Вот как теперь выглядит поиск всех дочерних элементов нужного типа:
Еще раз обращу внимание на то, что поиск выполняется на всю глубину вложенности.
Часто возникает задача – найти родительский элемент определенного класса, или который реализует определенный интерфейс. Вот метод, который это делает:
Применение этого метода очень просто:
Несколько раз встречал в форумах вопросы, связанные с тем, что поиск элемента по имени не работает в глубину сквозь рамки шаблонов. Поскольку сам сталкивался с такой проблемой, то сделал расширение, которое обходит это ограничение:
Думаю, пример тут приводить излишне, и так все понятно :-).
Описанные расширения очень просты, но их использование помогает сделать код существенно более понятным и изящным.
Если Вы программируете с использованием WPF, то, возможно, Вам будет интересно:
Я уже писал о пользе и удобстве расширений, когда рассказывал о расширениях для класса Exception, повторяться не буду. А расскажу о нескольких очень простых расширениях для класса FrameworkElement, которые очень облегчают жизнь в некоторых сценариях.
Получение всех дочерних элементов
Этот метод позволяет рекурсивно получить все дочерние элементы в виде перечисления./// <summary>
/// Получить перечисление дочерних визуальных объектов (рекурсивное перечисление)
/// </summary>
static public IEnumerable<FrameworkElement> GetTree(this FrameworkElement e)
{
if (e == null)
return new FrameworkElement[] { };
int total = VisualTreeHelper.GetChildrenCount(e) ;
return new FrameworkElement[] { e }
.Concat(
Enumerable.Range(0, total)
.SelectMany(x => GetTree(VisualTreeHelper.GetChild(e, x)
as FrameworkElement)))
.Where(x => x != null);
}
Хочу обратить внимание, что перечисление – это Linq выражение, которое вычисляется лениво, т.е. если выполняется перебор не всех элементов, то лишних обращений к методам VisualTreeHelper не происходит.
Вот как теперь выглядит поиск всех дочерних элементов нужного типа:
var boxes = myGryd.GetTree().OfType<CheckBox>().ToList();
Еще раз обращу внимание на то, что поиск выполняется на всю глубину вложенности.
Поиск родителя
Часто возникает задача – найти родительский элемент определенного класса, или который реализует определенный интерфейс. Вот метод, который это делает:
/// <summary>
/// Получить ближайший родительский визуальный объект заданного типа
/// </summary>
static public T GetAncestor<T>(this DependencyObject e)
where T : DependencyObject
{
if (e == null)
return null;
var parent = (DependencyObject)VisualTreeHelper.GetParent(e);
/// Идем вверх по родителям, пока не найдем ScrollViewer
while ((parent != null) && !(parent is T))
{
parent = (DependencyObject)VisualTreeHelper.GetParent(parent);
}
return (T)parent;
}
Применение этого метода очень просто:
var myTree = node.GetAncestor<TreeView>();
Поиск элемента по имени
Несколько раз встречал в форумах вопросы, связанные с тем, что поиск элемента по имени не работает в глубину сквозь рамки шаблонов. Поскольку сам сталкивался с такой проблемой, то сделал расширение, которое обходит это ограничение:
/// <summary>
/// усложненный вариант поиска элемента по имени
/// сначала вызывается стандартный метод
/// если он ничего не вернет, то ищем сами
/// </summary>
public static object FindChildByName(this FrameworkElement e, string name)
{
return e.FindName(name) ??
e.GetTree().FirstOrDefault(x => x.Name == name);
}
Думаю, пример тут приводить излишне, и так все понятно :-).
И в заключение
Описанные расширения очень просты, но их использование помогает сделать код существенно более понятным и изящным.
Если Вы программируете с использованием WPF, то, возможно, Вам будет интересно:
Комментариев нет:
Отправить комментарий