středa 25. září 2013

Procházení VisualTree

Pokud potřebujete ve WPF pracovat s kontroly, máte k dispozici dva stromy - logický a vizuální, se kterými můžete pracovat. Zjednodušeně se dá říci, že:

  • Logický strom reprezentuje základní strukturu kontrolů, které jste si vytvořili v XAML či jinak.
  • Vizuální strom pak reprezentuje kompletní sadu která se vyyrenderuje na daném zobrazovacím zařízení.

Pokud tedy chcete něco najít, je vhodnější sáhnout po vizuálním stromu a tento prohledávat. Vzhledem k tomu, že (snad) všechny kontroly jsou odděděné od DependencyObject tak používám k tomuto účelu třídu s extension metodami, které injectují potřebnou funckionalitu přímo do třídy DependencyObject a jsou tak kdykoliv k dispozici.

Pozn. Zvykl jsem si umísťovat veškeré své extension metody do stejného namespace jako je cílová třída, do které se injektují. Uznávám, že to není úplně čisté řešení - může dojít ke konfliktům, při přechodu na novější verzi .NET, pokud by Microsoft použil stejně pojmenované metody. Ale jsem líný (asi jako každý programátor) a vyhovuje mi, že mohu používat extensions kdekoliv bez toho abych si musel vzpomenout, jakou namespace musím přidat :-).


namespace System.Windows
{
    public static class DependencyObjectExtensions
    {

        public static T FindFirstVisualChild<T>(this DependencyObject current) where T : DependencyObject
        {
            if (current == null) return null;
            var children = new List<DependencyObject> { current };
            var currentIndex = 0;
            while (currentIndex < children.Count)
            {
                current = children[currentIndex];
                for (var i = 0; i < VisualTreeHelper.GetChildrenCount(current); i++) {
                    var child = VisualTreeHelper.GetChild(current, i);
                    if (child is T) {
                        return (T) child;
                    }
                    children.Add(child);
                }
                currentIndex++;
            }
            return null;
        }

        public static T FindVisualParent<T>(this DependencyObject element) where T : DependencyObject
        {
            var parent = element;
            while (parent != null) {
                if (parent is T) {
                    return (T)parent;
                }
                parent = VisualTreeHelper.GetParent(parent);
            }
            return null;
        }
        
    }
}

Pokud někde v kódu potřebujete najít rodiče nebo potomka (prvního při hledání do šířky) daného typu, stačí provolat výše uvedené metody. Daly by se zapsat i elegantněji přes rekurzi, ale v době psaní jsem měl zrovna náladu na toto řešení :-).


Žádné komentáře:

Okomentovat