Monday, February 15, 2010

Know Your Parents and Grandparents

In programming Silverlight I have come across situations where a control or framework element needs to access information on its parent. The code to get information off your direct parent is simple:


  1.             var oContent = this.Parent.ReadLocalValue(Content);
for example.

The real power of Silverlight is in the declarative nesting of XAML elements, and that means that the type of your direct parent may not always be known. To clarify, if you drop a control on a Page, then the Page object will be your parent. However if you then wrap your control in a ScrollViewer, the ScrollViewer becomes your parent and the Page becomes your grandparent.

To avoid all these issues I include the following code in most of my Silverlight projects:

  1.         public virtual T ParentOfType<T>() where T : FrameworkElement
  2.         {
  3.             Type oType = typeof(T);
  4.             var oParent = Parent as FrameworkElement;
  5.             while (oParent != null)
  6.             {
  7.                 if (oType.IsInstanceOfType(oParent))
  8.                     return oParent as T;
  9.  
  10.                 if (oParent.GetType() == oType)
  11.                     return oParent as T;
  12.  
  13.                 oParent = oParent.Parent as FrameworkElement;
  14.             }
  15.             return null;
  16.         }

Recently I needed to access the Navigation Service that is part of a Silverlight Page object. Using the method above I wrote this code to access the service:

  1.         void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  2.         {
  3.             var oPage = ParentOfType<Page>();
  4.  
  5.             if ( oPage != null )
  6.                 oPage.NavigationService.Navigate(new Uri(NavigateUri, UriKind.RelativeOrAbsolute));
  7.         }

Now regardless where this control is placed in the XAML, the Navigation Service will execute correctly.

2 comments:

Bart said...

In my opinion a control should never, ever, ever know or use a parent. That is modelling in the wrong direction and does not honor encapsulation.
I'm not sure what you want to accomplish , but it can probably designed differently.

Colin Eberhardt said...

Hi Steve,

You might be interested in Linq-to-Visual Tree:

http://www.scottlogic.co.uk/blog/colin/2010/03/linq-to-visual-tree/

You can find grandparent via the Ancestors extension method, e.g.

// the ancestors for 'TextBoxFour' that are StackPanels
var stackPanelAncestors = TextBoxFour.Ancestors();

Regards,
Colin E.