Click here to Skip to main content
Licence CPOL
First Posted 28 Jul 2009
Views 50,129
Bookmarked 6 times

Silverlight DataContext Changed Event

By | 28 Jul 2009 | Technical Blog
One known issue with Silverlight is that the DataContext bound to a control may change, but there is no readily available change event. Unlike WPF, you don't have an explicit event to register with in order to track changes.
A Technical Blog article. View original blog here.[^]

One known issue with Silverlight is that the DataContext bound to a control may change, but there is no readily available change event. Unlike WPF, you don't have an explicit event to register with in order to track changes. This becomes a problem in controls like the DataGrid control which reuses the same control instances for each page. Even though fresh data is bound, if your control isn't aware that the data context changed, it will keep stale content.

If you search online you'll find the solution is simple: you create a dependency property that is actually based on the data context (call it a "dummy" property) and then register for changes to that property. I was glad to find the solution but wanted something a little more reusable (remember, I like the DRY principle: don't repeat yourself, so when I find myself writing the same line of code more than once I have to go back and refactor).

The solution? I was able to find something that I think works well and involves an interface and a static class.

First, I want to identify when a control should be aware of changes to DataContext and also provide a method to call when this happens. That was easy enough. I created IDataContextChangedHandler and defined it like this:

public interface IDataContextChangedHandler<T> where T: FrameworkElement 
{
   void DataContextChanged(T sender, DependencyPropertyChangedEventArgs e);
}

As you can see, it is a simple interface. A method is called with the sender (which will presumably be the control itself) and the arguments for a dependency property changed event. It is typed to T, of course.

Next, I used generics to create a base class that manages the "fake" dependency property:

public static class DataContextChangedHelper<T> where T: FrameworkElement, IDataContextChangedHandler<T>
{
    private const string INTERNAL_CONTEXT = "InternalDataContext"; 

    public static readonly DependencyProperty InternalDataContextProperty =
        DependencyProperty.Register(INTERNAL_CONTEXT,
                                    typeof(Object),
                                    typeof(T),
                                    new PropertyMetadata(_DataContextChanged));

    private static void _DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        T control = (T)sender;
        control.DataContextChanged(control, e);
    }

    public static void Bind(T control)
    {
        control.SetBinding(InternalDataContextProperty, new Binding());
    }
}

As you can see, the class does a few things and works for any framework element, which is a "basic building block" that supports binding. It is typed to the FrameworkElement but also requires that the target implements IDataContextChangedHandler. It creates a dependency property. Because the data context can be any object, the type of the dependency is object, but the type of the parent is the framework element itself ("T"). When something happens to the property, it will invoke _DataContextChanged.

The event handler is sent the control that raised the event as well as the arguments for the old and new properties in the data context. We simply cast the sender back to its original type of T. Then, because we know it implements IDataContextChangedHandler, we can simply call DataContextChanged.

Finally, there is a static call to bind the control itself.

Now let's put the pieces together. Let's say you have a control that makes a gauge based on a data value, and you want to put the control in the grid. You need to know when the DataContext changes, because you will update your gauge. The control will look like this:

public partial class Gauge : IDataContextChangedHandler<Gauge> 
{
   public Gauge() 
   {
      InitializeComponent();
      DataContextChangedHelper<Gauge>.Bind(this); 
   }

   public void DataContextChanged(Gauge sender, DependencyPropertyChangedEventArgs e)
   {
      if (e.NewValue != null)
      {
         int gaugeLevel = (int)e.NewLevel;
         _UpdateImage(gaugeLevel);
      } 
   }
}

And there you have it - to register for the data context changing, we simply implemented IDataContextChangedHandler and then registered by calling Bind in our constructor.

Jeremy Likness

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Jeremy Likness

Architect
Wintellect
United States United States

Member

Follow on Twitter Follow on Twitter
Jeremy Likness is a Microsoft Silverlight MVP who works as Project Manager and Senior Consultant for Wintellect with 15 years of experience developing enterprise applications. He has worked with software in multiple verticals ranging from insurance, health and wellness, supply chain management, and mobility. His primary focus for the past decade has been building highly scalable web-based solutions using the Microsoft technology stack with a focus on Silverlight since version 2.0.
 
Prior to Wintellect, Jeremy was Director of Information Technology and served as development manager and architect for AirWatch, LLC, where he helped the company grow and solidify its position as one of the leading wireless technology solution providers in the United States by managing the development of their product portfolio that includes public HotSpot solutions and a management console for enterprise grade wireless networks, mobile devices, and their consumers. A fluent Spanish speaker, Jeremy served as Director of Information Technology for Hispanicare, where he architected a multi-lingual content management system for the company's Hispanic-focused online diet program. Jeremy accepted his role there after serving as Development Manager for Manhattan Associates, a software company that provides supply chain management solutions.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionCannot access value in DataContextChanged event Pinmembermac4_life6:48 17 Jan '12  
GeneralMy vote of 5 PinmemberSpazMoose10:07 11 May '11  
GeneralA more generic solution Pinmemberzlezj1:39 16 Jun '10  
General错误 1 不能将类型“Gauge”用作泛型类型或方法“IDataContextChangedHandler”中的类型形参“T Pinmembertjmxf22:41 10 Jan '10  
Generalbinding() cannot find namespace Pinmembertjmxf22:20 10 Jan '10  
GeneralRe: binding() cannot find namespace PinassociateJeremy Likness1:05 11 Jan '10  
GeneralRe: binding() cannot find namespace Pinmembertjmxf14:24 11 Jan '10  
GeneralRe: binding() cannot find namespace Pinmembertjmxf14:27 11 Jan '10  
GeneralReally "simple" solution PinmemberLauren Lilly5:14 4 Aug '09  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120517.1 | Last Updated 28 Jul 2009
Article Copyright 2009 by Jeremy Likness
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid