title: WPF Attached Property Content Control
body: <p>I have an attached property to provide context help for my varying uielements in my WPF application.</p>
  
  <p>The attached property is set to the child property of a border control that is the Child property of a popup control that is used to provide context sensitive help.</p>
  
  <p>Problem is the color foreground color defaults to random things depending on what UIEelment to which the attached property is attached.</p>
  
  <p>As guess I think this is happening because the content property is using the visual tree of the attached control. </p>
  
  <p>How can I force the content property to reevaluate the default properties fore foreground, font etc?</p>
  
  <p>This is based on this article: <a href="http://www.codeproject.com/Articles/59535/A-Simple-Integrated-WPF-Help-System" rel="nofollow">http://www.codeproject.com/Articles/59535/A-Simple-Integrated-WPF-Help-System</a></p>
  
  <p>Ultimately it is probably my derived popup class that needs fixing.  I've been looking for a better way to implement my popup but haven't found it yet.</p>
  
  <p>All the fun code is found below. starting with the popup class.</p>
  
  <p>Thanks for any assistance.</p>
  
  <pre><code>public class HelpPopup : Popup
  {
      private readonly ContentPresenter mContentPresenter = new ContentPresenter();
      private readonly Border mBorder = new Border();
      private readonly ContentControl mControl = new ContentControl();
  
      public HelpPopup()
      {
          mBorder.Child = mContentPresenter;
          mControl.Content = mBorder;
          Child = mControl;
  
          // no background for content control...
          mControl.Background = null;
          mControl.Foreground = SystemColors.ControlTextBrush;
  
          AllowsTransparency = true;
      }
  
      #region Content Dependency Property
  
      public static readonly DependencyProperty ContentProperty = ContentControl.ContentProperty.AddOwner(typeof (HelpPopup), new PropertyMetadata(default(object), ContentChanged));
  
      private static void ContentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
      {
          HelpPopup src = obj as HelpPopup;
  
          if (src != null)
          {
              // simply update content to match that what's being set..
              src.mContentPresenter.Content = e.NewValue;
          }
      }
  
      public object Content
      {
          get
          {
              object res = default(object);
  
              if (CheckAccess() != false)
              {
                  res = (object) GetValue(ContentProperty);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; res = (object) GetValue(ContentProperty));
              }
  
              return res;
          }
          set
          {
              if (CheckAccess() != false)
              {
                  SetValue(ContentProperty, value);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; SetValue(ContentProperty, value));
              }
          }
      }
  
      #endregion
  
      #region Background Dependency Property
  
      public static readonly DependencyProperty BackgroundProperty = 
                      Border.BackgroundProperty.AddOwner(typeof (HelpPopup), new PropertyMetadata(default(Brush), BackgroundChanged));
  
      private static void BackgroundChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
      {
          HelpPopup src = obj as HelpPopup;
  
          if (src != null)
          {
              src.mBorder.Background = e.NewValue as Brush;
          }
      }
  
      public Brush Background
      {
          get
          {
              Brush res = default(Brush);
  
              if (CheckAccess() != false)
              {
                  res = (Brush) GetValue(BackgroundProperty);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; res = (Brush) GetValue(BackgroundProperty));
              }
  
              return res;
          }
          set
          {
              if (CheckAccess() != false)
              {
                  SetValue(BackgroundProperty, value);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; SetValue(BackgroundProperty, value));
              }
          }
      }
  
      #endregion
  
      #region Foreground Dependency Property
  
      public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner(
          typeof (HelpPopup), new FrameworkPropertyMetadata(Brushes.Black, ForegroundChanged));
  
      private static void ForegroundChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
      {
          HelpPopup src = obj as HelpPopup;
  
          if (src != null)
          {
              src.mControl.Foreground = e.NewValue as Brush;
          }
      }
  
      public Brush Foreground
      {
          get
          {
              Brush res = default(Brush);
  
              if (CheckAccess() != false)
              {
                  res = (Brush) GetValue(ForegroundProperty);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; res = (Brush) GetValue(ForegroundProperty));
              }
  
              return res;
          }
          set
          {
              if (CheckAccess() != false)
              {
                  SetValue(ForegroundProperty, value);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; SetValue(ForegroundProperty, value));
              }
          }
      }
  
      #endregion
  
      #region BorderBrush Dependency Property
  
      public static readonly DependencyProperty BorderBrushProperty = Border.BorderBrushProperty.AddOwner(
          typeof (HelpPopup),
          new PropertyMetadata(default(Brush), BorderBrushChanged));
  
      private static void BorderBrushChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
      {
          HelpPopup src = obj as HelpPopup;
  
          if (src != null)
          {
              src.mBorder.BorderBrush = e.NewValue as Brush;
          }
      }
  
      public Brush BorderBrush
      {
          get
          {
              Brush res = default(Brush);
  
              if (CheckAccess() != false)
              {
                  res = (Brush) GetValue(BorderBrushProperty);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; res = (Brush) GetValue(BorderBrushProperty));
              }
  
              return res;
          }
          set
          {
              if (CheckAccess() != false)
              {
                  SetValue(BorderBrushProperty, value);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; SetValue(BorderBrushProperty, value));
              }
          }
      }
  
      #endregion
  
      #region BorderThickness Dependency Property
  
      public static readonly DependencyProperty BorderThicknessProperty = Border.BorderThicknessProperty.AddOwner(
          typeof (HelpPopup),
          new PropertyMetadata(default(Thickness), BorderThicknessChanged));
  
      private static void BorderThicknessChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
      {
          HelpPopup src = obj as HelpPopup;
  
          if (src != null)
          {
              src.mBorder.BorderThickness = (Thickness) e.NewValue;
          }
      }
  
      public Thickness BorderThickness
      {
          get
          {
              Thickness res = default(Thickness);
  
              if (CheckAccess() != false)
              {
                  res = (Thickness) GetValue(BorderThicknessProperty);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; res = (Thickness) GetValue(BorderThicknessProperty));
              }
  
              return res;
          }
          set
          {
              if (CheckAccess() != false)
              {
                  SetValue(BorderThicknessProperty, value);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; SetValue(BorderThicknessProperty, value));
              }
          }
      }
  
      #endregion
  
      #region CornerRadius Dependency Property
  
      public static readonly DependencyProperty CornerRadiusProperty = Border.CornerRadiusProperty.AddOwner(
          typeof (HelpPopup),
          new PropertyMetadata(default(CornerRadius), CornerRadiusChanged));
  
      private static void CornerRadiusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
      {
          HelpPopup src = obj as HelpPopup;
  
          if (src != null)
          {
              src.mBorder.CornerRadius = (CornerRadius) e.NewValue;
          }
      }
  
      public CornerRadius CornerRadius
      {
          get
          {
              CornerRadius res = default(CornerRadius);
  
              if (CheckAccess() != false)
              {
                  res = (CornerRadius) GetValue(CornerRadiusProperty);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; res = (CornerRadius) GetValue(CornerRadiusProperty));
              }
  
              return res;
          }
          set
          {
              if (CheckAccess() != false)
              {
                  SetValue(CornerRadiusProperty, value);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; SetValue(CornerRadiusProperty, value));
              }
          }
      }
  
      #endregion
  
      #region Padding Dependency Property
  
      public static readonly DependencyProperty PaddingProperty = Border.PaddingProperty.AddOwner(
          typeof (HelpPopup),
          new PropertyMetadata(default(Thickness), PaddingChanged));
  
      private static void PaddingChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
      {
          HelpPopup src = obj as HelpPopup;
  
          if (src != null)
          {
              src.mBorder.Padding = (Thickness) e.NewValue;
          }
      }
  
      public Thickness Padding
      {
          get
          {
              Thickness res = default(Thickness);
  
              if (CheckAccess() != false)
              {
                  res = (Thickness) GetValue(PaddingProperty);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; res = (Thickness) GetValue(PaddingProperty));
              }
  
              return res;
          }
          set
          {
              if (CheckAccess() != false)
              {
                  SetValue(PaddingProperty, value);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; SetValue(PaddingProperty, value));
              }
          }
      }
  
      #endregion
  
      static HelpPopup()
      {
          DefaultStyleKeyProperty.OverrideMetadata(typeof(HelpPopup), new FrameworkPropertyMetadata(typeof(HelpPopup)));
      }
  </code></pre>
  
  <p>The attached property is defined here:</p>
  
  <pre><code>class Help
  {
      struct HelpActiveInfo
      {
          private EventHandler Handler { get; set; }
          private UIElement Element { get; set; }
          private DependencyPropertyDescriptor Descriptor { get; set; }
  
          public HelpActiveInfo(UIElement element, DependencyProperty property, EventHandler handler) : this()
          {
              if (element == null)
                  throw new InvalidArgumentValue("element", "element cannot be null");
  
              Element = element;
  
              if (handler == null)
                  throw new InvalidArgumentValue("handler", "handler cannot be null");
  
              Handler = handler;
  
              if (property == null)
                  throw new InvalidArgumentValue("property", "property cannot be null");
  
              Descriptor = DependencyPropertyDescriptor.FromProperty(property, typeof(UIElement));
          }
  
          public void AddHandler()
          {
              if (Handler != null)
              {
                  if (Descriptor != null)
                  {
                      if (Element != null)
                          Descriptor.AddValueChanged(Element, Handler);
                  }
              }
          }
  
          public void RemoveHandler()
          {
              if (Handler != null)
              {
                  if (Descriptor != null)
                  {
                      if (Element != null)
                          Descriptor.RemoveValueChanged(Element, Handler);
                  }
              }
          }
      }
  
      private readonly static Dictionary&lt;UIElement, HelpActiveInfo&gt; smChangeMap = new Dictionary&lt;UIElement, HelpActiveInfo&gt;(); 
  
      #region Help Attached Property
  
      public static readonly DependencyProperty HelpProperty = DependencyProperty.RegisterAttached("Help", typeof(UIElement), typeof(Help), new PropertyMetadata(default(UIElement), HelpChanged));
  
      private static void HelpChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
      {
          UIElement ui = obj as UIElement;
  
          if (ui != null)
          {
              HelpActiveInfo hi;
              HelpWindow h = GetParentWindow(ui) as HelpWindow;
  
              if (smChangeMap.TryGetValue(ui, out hi) != false)
              {
                  hi.RemoveHandler();
                  smChangeMap.Remove(ui);
              }
  
              // if have help window, and the new help is not null
              // then monitor help active property to decide when to 
              // add adorner to the element....
              if ((h != null) &amp;&amp; (e.NewValue != null))
              {
                  hi = new HelpActiveInfo(h, HelpWindow.HelpActiveProperty,
                      (sender, args) =&gt;
                      {
                          AdornerLayer al = AdornerLayer.GetAdornerLayer(ui);
  
                          if (al != null)
                          {
                              Adorner[] adorners = al.GetAdorners(ui);
  
                              // remove and existing help adorners
                              if (adorners != null)
                              {
                                  List&lt;Adorner&gt; rl = adorners.Where(a =&gt; a.GetType() == typeof (HelpAdorner)).ToList();
  
                                  foreach (var r in rl)
                                      al.Remove(r);
                              }
  
                              // no adorners, add new one if needed
                              if (h.HelpActive != false)
                              {
                                  HelpAdorner ha = new HelpAdorner(ui);
  
                                  al.Add(ha);
                              }
                          }
                      });
  
                  smChangeMap[ui] = hi;
                  smChangeMap[ui].AddHandler();
              }
          }
      }
  
      public static void SetHelp(DependencyObject element, UIElement value)
      {
          element.SetValue(HelpProperty, value);
      }
  
      public static UIElement GetHelp(DependencyObject element)
      {
          return (UIElement)element.GetValue(HelpProperty);
      }
      #endregion
  
      #region Glow Brush Attached Property
      public static readonly DependencyProperty GlowBrushProperty = DependencyProperty.RegisterAttached(
          "GlowBrush", typeof (Brush), typeof (Help), new PropertyMetadata(new SolidColorBrush(Colors.Red) { Opacity = 0.3 }));
  
      public static void SetGlowBrush(DependencyObject element, Brush value)
      {
          element.SetValue(GlowBrushProperty, value);
      }
  
      public static Brush GetGlowBrush(DependencyObject element)
      {
          return (Brush) element.GetValue(GlowBrushProperty);
      }
      #endregion
  
      #region helpers
      public static Window GetParentWindow(DependencyObject child)
      {
          Window res = null;
  
          if (child != null)
          {
              DependencyObject parentObject = VisualTreeHelper.GetParent(child);
  
              res = parentObject as Window ?? GetParentWindow(parentObject);
          }
  
          return res;
      }
      #endregion
  }
  </code></pre>
  
  <p>A help window class manages the individual help items:</p>
  
  <pre><code>public class HelpWindow : Window
  {
      private DependencyObject CurrentHelpObject { get; set; }
      private HelpPopup CurrentHelpPopup { get; set; }
  
      public HelpWindow()
      {
          CommandBindings.Add(new CommandBinding(ApplicationCommands.Help,
              (x, y) =&gt;
              {
                  HelpActive = (HelpActive == false);
              },
              (x, y) =&gt;
              {
                  y.CanExecute = true;
              }));
  
          Loaded += OnLoaded;
      }
  
      private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
      {
          InitHelpSystem(this);
      }
  
      private void InitHelpSystem(DependencyObject obj)
      {
          // Continue recursive toggle. Using the VisualTreeHelper works nicely.
          for (int x = 0; x &lt; VisualTreeHelper.GetChildrenCount(obj); x++)
          {
              DependencyObject child = VisualTreeHelper.GetChild(obj, x);
              InitHelpSystem(child);
          }
  
          UIElement help = obj.GetValue(Help.HelpProperty) as UIElement;
  
          if (help != null)
          {
              // force a reset of the property because window is up and running
              obj.SetValue(Help.HelpProperty, null);
              obj.SetValue(Help.HelpProperty, help);
          }
      }
  
      // Return the result of the hit test to the callback.
      private readonly Stack&lt;DependencyObject&gt; mHitTestCollection = new Stack&lt;DependencyObject&gt;();
  
      public HitTestResultBehavior HitTestResult(HitTestResult result)
      {
          DependencyObject d = result.VisualHit;
  
          if (d != null)
              mHitTestCollection.Push(d);
  
          // Set the behavior to return visuals at all z-order levels. 
          return HitTestResultBehavior.Continue;
      }
  
      private void OnMouseMove(object sender, MouseEventArgs e)
      {
          object content = null;
          DependencyObject checkHelpObject = null;
          List&lt;UIElement&gt; dl = new List&lt;UIElement&gt;();
  
          mHitTestCollection.Clear();
  
          // You can check the HelpActive property if desired, however 
          // the listener should not be hooked up so this should not be firing
          VisualTreeHelper.HitTest(sender as Visual, null, new HitTestResultCallback(HitTestResult), new PointHitTestParameters(e.GetPosition(this)));
  
          // walk the list find the objects (or the parents that have 
          while (mHitTestCollection.Count &gt; 0)
          {
              DependencyObject d = mHitTestCollection.Pop();
  
              if (d != null)
              {
                  object c = null;
                  UIElement oc = null;
  
                  // find the content property for this one...
                  while ((c == null) &amp;&amp; (Equals(d, this) == false))
                  {
                      oc = d as UIElement;
  
                      if (oc != null)
                          c = oc.GetValue(Help.HelpProperty) as DependencyObject;
  
                      d = VisualTreeHelper.GetParent(d);
                  }
  
                  if (c != null)
                  {
                      // remove the containing control if it already is n the list
                      if (dl.Contains(oc) != false)
                          dl.Remove(oc);
  
                      // then add it to the list
                      // this ensures that top of the list is always the lowest seen 
                      // object that implements the attached property
                      dl.Add(oc);
                  }
              }
          }
  
          // go get the object to use
          checkHelpObject = dl.FirstOrDefault();
  
          // fetch content
          content = (checkHelpObject != null) ? checkHelpObject.GetValue(Help.HelpProperty) : null;
  
          // and process fetched content...
          if ((content == null) &amp;&amp; (CurrentHelpPopup != null))
          {
              CurrentHelpPopup.IsOpen = false;
              CurrentHelpPopup = null;
              CurrentHelpObject = null;
          }
          else
          {
              if ((content != null) &amp;&amp; (Equals(CurrentHelpObject, checkHelpObject) == false))
              {
                  CurrentHelpObject = checkHelpObject;
  
                  // New visual "stack" hit, close old popup, if any
                  if (CurrentHelpPopup != null)
                      CurrentHelpPopup.IsOpen = false;
  
                  CurrentHelpPopup = new HelpPopup()
                                     {
                                         IsOpen = true,
                                         Content = content,
                                         AllowsTransparency = true,
                                         PopupAnimation = PopupAnimation.Fade,
                                         PlacementTarget = (UIElement)checkHelpObject
                                     };
              }
          }
      }
  
      #region HelpActive Dependency Property
  
      public static readonly DependencyProperty HelpActiveProperty =
          DependencyProperty.Register("HelpActive", typeof (bool), typeof (HelpWindow),
              new PropertyMetadata(default(bool), HelpActiveChanged));
  
      private static void HelpActiveChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
      {
          HelpWindow src = obj as HelpWindow;
  
          if (src != null)
          {
              if (src.CurrentHelpPopup != null)
                  src.CurrentHelpPopup.IsOpen = false;
  
              src.CurrentHelpObject = null;
              src.CurrentHelpPopup = null;
  
              // enable/disable mouse move handlers...
              if (((bool) e.OldValue) != false)
                  src.MouseMove -= src.OnMouseMove;
  
              if (((bool)e.NewValue) != false)
                  src.MouseMove += src.OnMouseMove;
          }
      }
  
      public bool HelpActive
      {
          get
          {
              bool res = default(bool);
  
              if (CheckAccess() != false)
              {
                  res = (bool) GetValue(HelpActiveProperty);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; res = (bool) GetValue(HelpActiveProperty));
              }
  
              return res;
          }
          set
          {
              if (CheckAccess() != false)
              {
                  SetValue(HelpActiveProperty, value);
              }
              else
              {
                  Dispatcher.Invoke(() =&gt; SetValue(HelpActiveProperty, value));
              }
          }
      }
  
      #endregion
  }
  </code></pre>