Tag Archives: MVVM

RelativeSource Binding with FindAncestor mode in Silverlight

Silverlight 3 introduced the RelativeSource Binding that is well known from WPF. Unfortunately Silverlight only supports the two modes Self and TemplatedParent.

image 

I don’t know why the FindAncestor mode is missing in Silverlight’s RelativeSource binding, but there is a real need for that in Silverlight too. I’m finding me quite often in a dead-end ifI want to set a binding to a command or value in the view model within a list box item template. The problem in the example below is that the list box item has its own data context set to an item in the items-collection.

image

In the case above I really need a relative binding to set a binding to the RemoveCommand and to the global Picklist collection as comboxbox source. On solution is to create a bidirectional relationship between the view models behind the scene in order to get a reference to the parent view model within the child view model. But this not the solution I was looking for.

After some googling I found a binding helper written by Colin Eberhardt which enables it to use a relative binding with help of an attached dependency property.

clip_image001

Explained in brief: Colin uses an attached property to set his own relative source binding configuration for an specific element. When the attached property becomes attached to the target element it adds a handler for the elements loaded event. Within the event handler, he walks up the visual tree to find the specified ancestor and constructs a binding expression between the source and target properties.

Based on Colin’s idea I’ve created a similar implementation which provides some different behaviors:

  • Support for a list of relative binding. This allows binding more than one property of an element.
  • It does not use a relay object in the middle of the binding. It is a pure binding without any custom code in between. This gain in a better performance, especially when using a storyboard to animate the data bound value.
  • I have added some special type of relative binding mode which is called as ParentDataContext mode. In that mode it walks up the visual tree until it finds a new data context. This is very helpful when having templates in an ItemsControl, ListBox or DataGrid.
  • Support for non-dependency property as source, e.g. direct usage of view model properties.
  • Support for property path syntax for the source property, e.g. DataContext.Person.Name
  • The AncestorType allows to set a base class as type criteria instead of the concrete type, e.g. if the concrete element is of type Grid, then the AncestorType can be set to Panel.
  • Allows to set the relative binding for attached properties too, e.g. to bind TooltipService.Tooltip property.
  • Allows to use OneWay or OneTime binding mode.
  • Allows to set any other binding parameter such as ValidatesOnNotifyDataErrors, ValidatesOnExceptions, ValidatesOnDataErrors, NotifyOnValidationError and ConverterCulture.
  • Enables to set the XAML namespace for controls which are not in the core-control assembly.

How to use

Let’s have a look at some examples.

1. Simply relative binding with ancestor type

<ComboBox>
    <local:BindingHelper.Binding>
            <local:RelativeSourceBinding Path="DataContext.Picklist"
                    TargetProperty="ItemsSource" RelativeMode="FindAncestor"
                    AncestorType="UserControl" />
    </local:BindingHelper.Binding>
</ComboBox>
 

 

2. Bind two or more properties by adding a list of binding definitions

<ComboBox>
    <local:BindingHelper.Binding>
        <local:BindingList>
            <local:RelativeSourceBinding Path="DataContext.Picklist"
                    TargetProperty="ItemsSource" RelativeMode="FindAncestor"
                    AncestorType="UserControl" />
            <local:RelativeSourceBinding Path="DataContext.Tooltip"
                    TargetProperty="(ToolTipService.ToolTip)" RelativeMode="FindAncestor"
                    AncestorType="UserControl" />
        </local:BindingList>
    </local:BindingHelper.Binding>
</ComboBox>

 

 

3. Bind any attached dependency property

<local:RelativeSourceBinding Path="DataContext.Tooltip"
        TargetProperty="(ToolTipService.ToolTip)" RelativeMode="FindAncestor"
        AncestorType="UserControl" />

 

 

4. Bind an attached dependency property from any assembly. Just specify the XAML namespace as in the example below

<local:RelativeSourceBinding Path="DataContext.Tooltip"
        TargetProperty="(DemoAttachedElement.Value)"
        TargetNamespace="clr-namespace:RelativeSourceBindingDemo;assembly=RelativeSourceBindingDemo"
        RelativeMode="FindAncestor" AncestorType="UserControl" />

 

 

5. Use the parent data context instead an ancestor type

<local:RelativeSourceBinding Path="RemoveCommand" TargetProperty="Command"
        RelativeMode="ParentDataContext" />

 

 

6. The mode ParentDataContext is set as default behavior. So in most cases you can just write xaml as following:

<Button Content="Remove" CommandParameter="{Binding}">
    <local:BindingHelper.Binding>
        <local:RelativeSourceBinding Path="RemoveCommand" TargetProperty="Command" />
    </local:BindingHelper.Binding>
</Button>

<ComboBox Grid.Column="1">
    <local:BindingHelper.Binding>
        <local:BindingList>
            <local:RelativeSourceBinding Path="Picklist" TargetProperty="ItemsSource"/>
            <local:RelativeSourceBinding Path="Tooltip" TargetProperty="(ToolTipService.ToolTip)" />
        </local:BindingList>
    </local:BindingHelper.Binding>
</ComboBox>

 

 

7. Use a converter and other binding settings

<local:RelativeSourceBinding Path="Application.IsEnabled"
        TargetProperty="Visibility" Converter="{StaticResource VisibilityConverter}"
        ValidatesOnNotifyDataErrors="True" ValidatesOnExceptions="True"
        ValidatesOnDataErrors="True" NotifyOnValidationError="True" />

 

Source Code & Demo Project

Here you can find the full source code within a demo project.

Advertisements
Tagged ,

Deep Zoom Collection Downloader

Just for fun I’ve implemented a Deep Zoom Tiles Downloader which recreates the original image from a deep zoom composition. Additionally the downloader can download the original composition from the website to your local disk. The downloader follows the principals of a pure MVVM application.

No, i have not too much time. I’m pretty well experienced with the MVVM pattern in Silverlight but not in the WPF runtime. To get more familiar with MVVM in WPF and to see the difference in a practical manner I decided to put the implementation into a ViewModel and to build a WPF-View on top of them.

The Application

In the Image Rebuilder tab, the application allows to set the deep zoom image settings or discover them automatically from a standard Deep Zoom Composer xml-file.

image

The downloader allows you to set each deep zoom composition parameter manually, because there are deep zoom compositions out there in the internet without having the standard Deep Zoom Composer XML format.

One nice example of such a collection is the “Matterhorn Deep Zoom Collection” created by Ronnie Saurenmann (Microsoft Switzerland).

image

Enter the following values to test the downloader with his collection:

 

In the Tiles Downloader tab, the application allows to set the root folder of the deep zoom composition (containing the folder levels 0, 1, 2, 3, 4, 5, etc), specifying the download target folder and to start the download.

image

 

ViewModel implementation

I’m using the Bitmap from the GDI to rebuild the image. A 32 bit RGB formatted bitmap uses 32 bit i.e. 4 bytes to store data for each pixel. So if you try to rebuild a large deep zoom composition with as example of 50000×50000 pixels then the memory the downloader needs to allocate dynamically for the image is very large 50000*50000*4 bytes which is roughly about 9 GB in memory. While this is not possible with GDI the downloader downloads the image in smaller portion so you might get an image portioned as following (example show image in 5 portions):

image

It should be very easy to build the final image with Photoshop or ImageMagick from the portioned tiles. Feel free to extend the downloader with a better approach while using a 3rd party image lib.

A note about the performance: the downloader rebuilds or downloads images tile-by-tile. I did not have enough time to do it parallel in several threads.

Summary

It’s quite the same to implement a ViewModel in WPF and Silverlight for such very small applications. The only difference I can see so far is the missing constraint for asynchron web request in WPF. This makes the implementation a little bit simpler. The binding expressions are identically to Silverlight, because I don’t need any special here. The command binding was easier to write because the Command property is supported from the ButtonBase natively (Silverlight will provide the same in version 4).

I know, this is a quite simple application and things get complicated when the application gets more complex.

Feedbacks are welcome.

Source code

You can download the source code here:

http://files.thekieners.com/blogcontent/2010/DeepZoomDownloader/source.zip

One final note

This downloader was implemented just for fun. Please consider copyrights when downloading deep zoom images.

Tagged , , ,

How to make Visifire Chart bindable to ViewModel

The Visifire chart control is very cool and handy, but there is one missing part: it does not support data binding. The Visifire team confirmed that the current release (version 3.0.1.0) is not bindable and they are working on this issue with high priority: (http://www.visifire.com/forums/index.php?showtopic=1631).
Currently I’m working in a large Silverlight project where we use the Visifire chart and doing straight MVVM.

First attempt

In my first attempt I created a BindableDataSeries class inherited from DataSeries like the same concept I did to make the Silverlight Toolkit charts mulit-series bindable. See here: http://blog.thekieners.com/2010/02/07/databinding-multi-series-charts

I added a DataPointSource of type IEnumarable and DataPointTemplate of type DataTemplate to the BindableDataSeries class. Both properties are bindable. When the SeriesSource is set then DataSeries elements are generated based on the data template in the SeriesTemplate property in the same manner as an ItemsControl generates its items. Finally the generated DataSeries are added to the Series collection.

Now I’m able to bind the data point to my ViewModel as following:

    <vc:Chart>
        <vc:Chart.Series>
            <local:BindableDataSeries DataPointSource="{Binding SalesData}" >
                <local:VisifireBindableDataSeries.DataPointTemplate>
                    <DataTemplate>
                        <vc:DataPoint AxisXLabel="{Binding SalesName}" 
                                      YValue="{Binding SalesPerformance}"/>
                    </DataTemplate>
                </local:VisifireBindableDataSeries.DataPointTemplate>               
            </local:BindableDataSeries>
        </vc:Chart.Series>
    </vc:Chart>

Unfortunately there is still one problem. The DataPoint is an UIElement which is never part of the visual tree and binding gets not applied, because the binding is not executed when the element is not in the visual tree. As a result the DataPoint properties remain empty.

Second attempt

It shows to me that the only way to setup a Visifire Chart with data from a ViewModel is by writing code. How to do this without getting a strong reference between the View and the ViewModel? Remember to MVVM concepts, when creating a strong reference between View and ViewModel (either in one or both ways) you will lose many advantages the MVVM serves you.

In my second attempt I’ve created a Visifire Chart Wrapper which contains the most common properties of the Visifire Chart as bondable dependency properties.

Let me explain it step by step.

The control is inherited from System.Windows.Control and not from ContentControl, because there is no need for a composite control. The VisifireWrapper uses a static template originated from the code and not from the generic.xaml, because I don’t want to allow to re-template it. In the OnApplyTemplate override is a check implemented to guarantee this. Microsoft uses the same concept in the ViewBox control contained in the Silverlight Toolkit (https://silverlight.svn.codeplex.com/svn/Release/Silverlight3/Source/Controls.Toolkit/Viewbox/Viewbox.cs).

    /// <summary>
    /// VisifireWrapper serves as an Visifre Chart Wrapper to get the Chart MVVM bindable.
    /// You must inherit from this class an implement OnUpdateChart method.
    /// </summary>
    public abstract class VisifireWrapper : Control
    {

        public VisifireWrapper()
        {
          // Load the default template
          this.Template = DefaultTemplate = XamlReader.Load(DefaultTemplateMarkup) as ControlTemplate;
          ApplyTemplate();
        }

        /// <summary>
        /// XAML markup used to define the write-once wrapper template.
        /// </summary>
        private const string DefaultTemplateMarkup =
            "<ControlTemplate " +
            "    xmlns='http://schemas.microsoft.com/client/2007'  " +
            "    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'  " +
            "    xmlns:local='clr-namespace:VisifireMvvmIntegrationDemo;assembly=VisifireMvvmIntegrationDemo' " +
            "    TargetType='local:VisifireWrapper' > " +
            "    <ContentPresenter  " +
            "        Content='{TemplateBinding Chart}'  " +
            "        HorizontalAlignment='Stretch'  " +
            "        VerticalAlignment='Stretch'  /> " +
            "</ControlTemplate> ";

        /// <summary>
        /// Gets or sets the default ControlTemplate of the VisifireWrapper.
        /// </summary>
        private ControlTemplate DefaultTemplate { get; set; }

        public sealed override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // Ensure the Template property never changes from the
            // DefaultTemplate, and only apply it one time.
            if (Template != DefaultTemplate)
                throw new InvalidOperationException("The template can only be applied one time.");
        }
    }

 

The template contains a ContentPresenter which is data bound to the Chart property. The Chart property is set in the OnUpdateChart method which I’m going to introduce in a minute.

As next I added the most commonly used Visifire properties as dependency properties. Please find the full source code of the beneath dependency properties in the attached source.

/// <summary>
/// Gets or sets a collection used to generate the data point series.
/// </summary>
public IEnumerable ChartSource
{
    get { return (IEnumerable)GetValue(ChartSourceProperty); }
    set { SetValue(ChartSourceProperty, value); }
}

/// <summary>
/// Gets or sets a collection used to generate the chart multiline-title.
/// </summary>
public IEnumerable TitlesSource
{
    get { return (IEnumerable)GetValue(TitlesSourceProperty); }
    set { SetValue(TitlesSourceProperty, value); }
}

/// <summary>
/// Gets or sets a collection used to generate X-Axes
/// </summary>
public IEnumerable AxesXSource
{
    get { return (IEnumerable)GetValue(AxesXSourceProperty); }
    set { SetValue(AxesXSourceProperty, value); }
}

/// <summary>
/// Gets or sets a collection used to generate Y-Axes
/// </summary>
public IEnumerable AxesYSource
{
    get { return (IEnumerable)GetValue(AxesYSourceProperty); }
    set { SetValue(AxesYSourceProperty, value); }
} 

ChartSource will point to a collection of chart points or a collection of multi chart data. Multi chart data is meant to be used when you want to defined the numbers of lines in a line-chart dynamically. The attached demo project contains an example of this.

TitlesSource points to a collection of title information. Visifire Charts allow you to define a title with multiple lines. Each item in the collection is meant to be one line in the chart title.

AxesXSource and AxesYSource points to collections of specific axes-captions or axes-settings.

The VisifireWrapper contains abstract method OnUpdateChart. When overridden in sub class it updates the Visifire Chart wrapped by the VisifireWrapper control. The method is called in the first layout pass after one of the sources ChartSource, TitlesSource, AxesXSource or AxesYSource has changed. This is unlike the ItemsControl where the child items gets generated when the source changes. Recently I discovered that the DataGrid works exactly the same way: it generates its items (rows, etc) in the first layout pass after the ItemsSource has changed. But why I do it that way? The answer is performance, as you can read in the next section.

The charts are very nice animated during the first load (this is quite import for us, because it is part of a bigger aim to serve a new user experience to our customers. But this is another story…). Unfortunately the Visifire Chart does not allow updating DataPoints in every circumstance. Changing existing DataPoints is not a problem until you want to add or remove DataPoints. When you add DataPoints to an existing series the chart does this animated but the output is not correct as you can see in the print screen below:

Before…

clip_image001

…and after adding one DataPoint

clip_image002

Adding or removing DataPoints is not possible so far (version 3.0.1.0). The solution is to remove all DataPoints and re-add it to the chart. In that way we lose the animation, because the chart does animate only on first time load or when updating existing points. Therefore our solution is quite simple: every time any of the chart sources changes a new chart control instance is created with new DataPoints. OnUpdateChart override looks like this:

public class ColumnChart : VisifireWrapper
{
    protected override void OnUpdateChart()
    {

        // init chart control
        Chart chart = new Chart();
        chart.Theme = "Theme2";
        chart.AnimatedUpdate = true;
        chart.AnimationEnabled = true;
        chart.View3D = true;

        // create data series when DataSource is set
        if (this.ChartSource != null)
        {
            // init data point series
            DataSeries series = new DataSeries();
            series.RenderAs = RenderAs.Column;
            series.ShadowEnabled = true;
            series.XValueType = ChartValueTypes.Date;
            series.YValueFormatString = "hh:mm tt";
            series.SelectionEnabled = false;
            chart.Series.Add(series);

            foreach (PointData item in this.ChartSource)
            {
                DataPoint dataPoint = new DataPoint();
                dataPoint.XValue = item.Date;
                dataPoint.YValue = item.Value;
                series.DataPoints.Add(dataPoint);
            }
        }

        // set the chart title
        if (this.TitlesSource != null)
        {
            foreach (string title in this.TitlesSource)
            {
                Title chartTitle = new Title();
                chartTitle.Text = title;
                chart.Titles.Add(chartTitle);
            }
        }

        // completely replace the chart control
        this.Chart = chart;

    }

}

To set the values for your needs, the Visifire Chart samples provide excellent demos as XAML and its very simple to translate it into imperative code.

At the end of the method you must set the Chart property with the new chart. The new chart gets data bound to the ContentPresenter through TemplateBinding.

Internals

Let’s have a look into the internals of the abstract class VisifireWrapper.

Below is the implementation of the ChartSource dependency property, served as a model for the other properties:

/// <summary>
/// Gets or sets a collection used to generate the data point series.
/// </summary>
public IEnumerable ChartSource
{
    get { return (IEnumerable)GetValue(ChartSourceProperty); }
    set { SetValue(ChartSourceProperty, value); }
}
public static readonly DependencyProperty ChartSourceProperty = 
    DependencyProperty.Register("ChartSource", 
        typeof(IEnumerable), 
        typeof(VisifireWrapper), 
        new PropertyMetadata(new PropertyChangedCallback(OnChartSourceChanged)));

private static void OnChartSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    IEnumerable oldValue = (IEnumerable)e.OldValue;
    IEnumerable newValue = (IEnumerable)e.NewValue;
    VisifireWrapper source = (VisifireWrapper)d;
    source.OnChartSourceChanged(oldValue, newValue);
}

protected virtual void OnChartSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
    _chartSourceCollectionChanged.SetEventSource(newValue);

    InvalidateChart();
}

 

In the changed handler are two things to do:

  • Set the event source for the WeakEventSource wrapper. The wrapper serves a weak event pattern to listen for CollectionChanged event in a non-memory-leaking way. In two previous post I explain in detail what the WeakEventSource and WeakEventListener serves you.
  • Secondly set an internal flag to invalidate the chart so it gets updated the next time the control renders its child.

You can easily implement additional dependency properties. When using the WeakEventSource you do not have to think about possible memory leaks when listen for source-events.

Summary

The VisifireWrapper is a very helpful base class to implement custom Visifire charts which are bindable to a ViewModel without having a strong reference between the View and ViewModel. The control supports the Silverlight content model except data-templating and encapsulates the weak-event-pattern. In addition the implementation shows the usage of the WeakEventSource implementation I did from a previous post.

Any feedbacks are welcome.

Demo & Source

Below you can find a demo project and the full source code of the VisifireWrapper and WeakEventSource. The demo contains a Single-Series and Multi-Series chart and a demo implementation for common chart types.

clip_image002

Tagged , , ,

WeakEventSource implementation

The Situation

Implementing custom controls with dependency properties binding to an IEnumarable object collection is not a developer’s daily task. Most of the time one would use the ItemsControl which already provides the ItemsSource property together with the ItemsTemplate property. But what when the functionality provided with the ItemsControl does not meet your requirement? Then you can implement your own dependency property for the source collection like the following:

#region DataPointsSource (DependencyProperty)

public IEnumerable DataPointsSource
{
    get { return (IEnumerable)GetValue(DataPointsSourceProperty); }
    set { SetValue(DataPointsSourceProperty, value); }
}
public static readonly DependencyProperty DataPointsSourceProperty =
    DependencyProperty.Register("DataPointsSource",
                        typeof(IEnumerable),
                        typeof(CustomControl),
                        new PropertyMetadata(new PropertyChangedCallback(OnDataPointsSourceChanged)));

private static void OnDataPointsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}  

#endregion

 

To fully support the Silverlight content model you should check whether the source collection implements INotifyCollectionChanged interface or not. If the source implements INotifyCollectionChanged the custom control code should response to this event and Add/Remove/Replace items accordingly. Attaching the necessary event as following can produce a serious memory leaking problem:

protected virtual void OnDataPointsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
    // implements INotifyCollectionChanged?
    var sourceCollectionChanged = newValue as INotifyCollectionChanged;
    if (sourceCollectionChanged != null)
    {
        // attach the event
        sourceCollectionChanged.CollectionChanged += OnCollectionChanged;
    }
}

 

In a previous post I explained in detail why there can be a memory leak and how we prevent it in our SL controls. The solution is quite the same as Microsoft uses in the Silverlight Toolkit. Additionally David Anson wrote an excellent article about leaking controls and the solution they use in the Silverlight Toolkit.

So, writing code using the WeakEventListener pattern described in the blog posts above it still a tedious task. First you have to detach the previous listener and secondly you have to create a new weak-listener for the new event source. This result in these lines of code:

protected virtual void OnDataPointsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
    // detach previous event source
    if (_weakEventListener != null)
    {
        //  detach WeakEventListener
        _weakEventListener.Detach();
        _weakEventListener = null;
    }

    // attach new WeakEventListener when new source implements INotifyCollectionChanged
    var sourceCollectionChanged = newValue as INotifyCollectionChanged;
    if ( sourceCollectionChanged != null)
    {
        // create WeakEventListener. Pass Listener and EventSource instance to it.
        var weakListener = new WeakEventListener<VisifireMvvmWrapperDemo, 
                                      INotifyCollectionChanged, 
                                      NotifyCollectionChangedEventArgs>(this, sourceCollectionChanged);
        // register handler
        sourceCollectionChanged.CollectionChanged += weakListener.OnEvent;
        // define event handler to handle the event.
        weakListener.OnEventAction = (instance, source, e) =>
        {
            // event handling code
            instance.OnCollectionChanged(instance, e);
        };
        // define action to detach the handler
        weakListener.OnDetachAction = (listener, source) =>
        {
            // unregister handler
            source.CollectionChanged -= listener.OnEvent;
        };
        // ensure there is no reference from the listener to the weakListener
        weakListener = null;
    }

}

Due to the fact that we use this pattern quite often for custom control development, I was looking for a solution which is easier to use.

Our solution

With an additional wrapper encapsulating the WeakEventListener our developers can now write just the following lines of code to get the same result:

private CollectionChangedWeakEventSource _sourceChanged;

public CustomControl()
{
    // setup wrapper for CollectionChanged
    _sourceChanged = new CollectionChangedWeakEventSource();
    _sourceChanged.CollectionChanged += new NotifyCollectionChangedEventHandler(sourceChanged_CollectionChanged);
}

protected virtual void OnDataPointsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
    // just set the event source to the wrapper
    _sourceChanged.SetEventSource(newValue);
}

void sourceChanged_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{

}
 

In the control constructor (or when I use the wrapper the first time) I set up an instance of the CollectionChangedWeakEventSource and attach the CollectionChanged event. In the OnDataPointsSourceChanged handler I just set the source object for the wrapper. A previous source collection (oldValue) gets detached from the wrapper automatically when you call SetEventSource with the new value. The object passed to the method SetEventSource gets attached when the object implements INotifyCollectionChanged.

The class CollectionChangedWeakEventSource is quite easy to understand. It wraps just one event and uses the WeakEventListener class for the weak event pattern.

public class CollectionChangedWeakEventSource : WeakEventSourceBase<INotifyCollectionChanged>
{
  protected override WeakEventListenerBase CreateWeakEventListener(INotifyCollectionChanged eventObject)
  {
        // create a WeakEventListener
        var weakListener = new WeakEventListener<CollectionChangedWeakEventSource, 
                                                 INotifyCollectionChanged, 
                                                 NotifyCollectionChangedEventArgs>(this, eventObject);
        weakListener.OnDetachAction = (listener, source) =>
        {
            source.CollectionChanged -= listener.OnEvent;
        };
        weakListener.OnEventAction = (instance, source, e) =>
        {
            // fire event
            if (instance.CollectionChanged != null)
                instance.CollectionChanged(source, e);
        };
        eventObject.CollectionChanged += weakListener.OnEvent;

        return weakListener;
  }

  public event NotifyCollectionChangedEventHandler CollectionChanged;

}
 

The base class WeakEventSourceBase contains the general code to make the custom wrapper as simple as possible. As a result it is quite easy to implement an event wrapper for another event like the PropertyChanged event. Additionally I wrote a code snippet which is available here.

At the end of this post you can find the source code of WeakEventSourceBase.

Some explanation to the code:

  • WeakEventSourceBase contains a check to verify whether the implementation is correct or not. This check is executed only if a debugger is attached. Please note: I don’t use the Conditional(“DEBUG”) attribute to enable/disable this check, because we put the WeakEventSourceBase class into a common framework lib and this lib is RELEASE compiled.
  • Calling SetEventSource(null) is the same as calling Detach()
  • When setting a new event source, the previous source gets detached
  • When setting an event source which does not implement the event, it does not get attached, but the EventSource property is set to this instance and an previous event source gets detached.
  • The class WeakEventListenerBase is new due to the refactoring that was needed to get a non-generic declaration for the CreateWeakEventListener method return value.

Summary

The WeakEventSource implementation is a weak-event-pattern implementation based on the WeakEventListener. The class helps us to reduce writing tedious code in custom control development, reduces memory leaks which are hard to find and last but not least it is easier to review custom control written that way, because a Reviewer must not have full knowledge about the weak-event pattern.

In a blog post later this week I’m going to demonstrate a complete custom control implementation using this WeakEventSource implementation.

 

Source Code

Here you can find the full source code.

image

WeakEventSourceBase class:

/// <summary>
/// Base class to wrap a specific event with the WeakEventListener.
/// </summary>
/// <typeparam name="TSource">The type of the event source.</typeparam>
public abstract class WeakEventSourceBase<TSource>
     where TSource : class
{

    /// <summary>
    /// A weak reference to the event source
    /// </summary>
    private WeakReference _weakEventSource;

    /// <summary>
    ///  A weak reference to the WeakEventListener instance.
    /// </summary>
    private WeakReference _weakListener;

    /// <summary>
    /// Gets the event source instance which this listener is using.
    /// </summary>
    /// <remarks>
    /// The reference to the event source is weak.
    /// </remarks>
    public object EventSource
    {
        get
        {
            if (_weakEventSource == null)
                return null;

            return this._weakEventSource.Target;
        }
    }

    /// <summary>
    /// Set the event source for this instance. 
    /// When passing a new event source it replaces the event source the 
    /// listener is listen for an event. When passing null/nothing is detaches 
    /// the previous event source from this event listener. 
    /// </summary>
    /// <param name="eventSource">The event source instance.</param>
    public void SetEventSource(object eventSource)
    {
        // the listener can just listen for one event source. 
        // Detach the previous event source
        this.Detach();

        // keep weak-reference to the the event source
        this._weakEventSource = new WeakReference(eventSource);

        TSource eventObject = eventSource as TSource;
        if (eventObject != null)
        {
           var weakListener = CreateWeakEventListenerInternal(eventObject);

           if (weakListener == null)
              throw new InvalidOperationException("The method CreateWeakEventListener must return a value.");

           // store the weak-listener as weak reference (for Detach method only)
           _weakListener = new WeakReference(weakListener);
        }
    }

    /// <summary>
    /// Does some debug-time checks and creates the weak event listener.
    /// </summary>
    /// <param name="eventObject">The event source instance</param>
    /// <returns>Return the weak event listener instance</returns>
    private WeakEventListenerBase CreateWeakEventListenerInternal(TSource eventObject)
    {
        #region Debug time checks

        // do some implementation checks when a debugger is attached
        if (Debugger.IsAttached)
        {
            // search in each type separately unitl we reach the type WeakEventSourceBase 
            //(because Reflection can not return private members in FlattenHierarchy.
            Type type = this.GetType();
            while ((!type.IsGenericType || type.GetGenericTypeDefinition() 
!= typeof(WeakEventSourceBase<>)) && type != typeof(object)) { BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; // get fields expect fields marked with CompilerGeneratedAttribute or derived
// from Delegate (events are delegate fields)
var queryFields = from f in type.GetFields(bindingFlags) where f.GetCustomAttributes(typeof(CompilerGeneratedAttribute),true).Count()==0 && !f.FieldType.IsSubclassOf(typeof(Delegate)) select f.Name; // get properties var queryProperties = from f in type.GetProperties(bindingFlags) select f.Name; var query = queryFields.Union(queryProperties); // The EventWrapper is intended to be used as a weak-event-wrapper. One should not add // additional members to this class, because of the possibilty to store the // WeakEventListener reference to a member.
// Is this the case the memory leak can still occur.
// Therefore, if any field or property is implemented, throw an exception as warning. if (query.Count() > 0) { // note: MessageBox.Show blocks unit tests throw new InvalidOperationException(string.Format("You should not add any other “ +
“implementation than overriding methods in the class {0}, because of “ +
                             “possible memory you can get within your application.", type.Name));
                }

                // continue search in base type
                type = type.BaseType;
            }
        }

        #endregion

        // create weak event listener
        return CreateWeakEventListener(eventObject);
    }

    /// <summary>
    /// When overridden in a derived class, it creates the weak event 
/// listener for the given event source.
/// </summary> /// <param name="eventObject">The event source instance to listen for an event</param> /// <returns>Return the weak event listener instance</returns> protected abstract WeakEventListenerBase CreateWeakEventListener(TSource eventObject); /// <summary> /// Detaches the event from the event source. /// </summary> public void Detach() { if (_weakListener != null) { // do it the GC safe way, because an object could potentially be reclaimed // for garbage collection immediately after the IsAlive property returns true WeakEventListenerBase target = _weakListener.Target as WeakEventListenerBase; if (target != null) target.Detach(); } _weakEventSource = null; _weakListener = null; } }
Tagged , , ,

Simple Weak Event Listener for Silverlight

The problem

When using normal CLR events, registering an event handler creates a strong reference from the event source to the listening object.

clip_image002

If the event-source (our ViewModel) has a longer lifetime than the listener and the listener gets released from the hosting app (in our case the visual tree) then it can’t be garbage collected, because the long living event-source instance has still a reference to the listener through the delegate.

clip_image004

Unfortunately, visual tree elements don’t provide an UnLoaded event. So I’m not able to unregister the event handler from the event-source. For example when using the event-source for multiple pages as listeners these are alive until the event source gets released. This is a hidden memory leak in our Silverlight application.

clip_image006

It is not something new and there are several solutions out there. Most of the solutions using Reflection or dynamic code generation via Reflection.Emit which is not working in Silverlight because of limited reflection permission in Silverlight or it suffer from performance issues.

But how does the Silverlight Toolkit Team deal with this problem? For example, listening for INotifyPropertyChanged or INotifyCollectionChanged in any control is a common task when implementing a custom control.

The solution

Indeed, the Silverlight Toolkit source contains a very simple solution for this problem. The class is called WeakEventListener but its visibility is internal. I just made a copy of the class (thanks to the Microsoft Public License) to use it in our project.

When using this class you should pay attention to two implementation detail otherwise the application still has memory leaks (it think this is the reason why Microsoft did not make the class public). I try to explain it in the rest of this post.

How it works

The WeakEventListener operates as an instance between the event-source and the listener and contains the event handling part.

clip_image008

The WeakEventListener associates the listener instance via a WeakReference. It’s very import that the listener just creates the WeakEventListener instance but never holds a reference to it. This together ensures that the listener can be garbage collected when the App instance releases the listener.  UPDATE 30.03.2010: Based on the feedback from Eric Garnier (see comments below) I did a small change in the WeakEventListener class. Internally the WeakEventListener has a reference back to the EventSource. This reference is needed to unregister the event handler as soon the listener gets released from the GC. This reference is now implemented as a WeakReference. Now, it doesn’t matter if you have the WeakEventListener referenced from the listener or not. The attached source code includes the changes.

 

clip_image010

How to use in code

// register for event (weak pattern)
EventSource longLivingInstance = this.DataContext as EventSource;

// create WeakEventListener. Pass Listener and EventSource instance to it.
var weakListener = new WeakEventListener<Listener, EventSource, 
PropertyChangedEventArgs>(this, longLivingInstance); // register handler longLivingInstance.PropertyChanged += weakListener.OnEvent; // define event handler to handle the event. weakListener.OnEventAction = (instance, source, e) => { // event handling code instance.longLivingInstance_PropertyChanged(instance, e); }; // define action to detach the handler weakListener.OnDetachAction = (listener, source) => { // unregister handler source.PropertyChanged -= listener.OnEvent; }; // ensure there is no reference from the listener to the weakListener weakListener = null;

Step-by-Step explained

First create an instance of the WeakEventListener with the relevant type arguments from the Listener, EventSource and EventArgs.

// create WeakEventListener. Pass Listener and EventSource instance to it.
var weakListener = new WeakEventListener<Listener, EventSource, 
PropertyChangedEventArgs>(this, longLivingInstance);
 

As next, register the OnEvent method from the weakListener as event-handler.

// register handler
longLivingInstance.PropertyChanged += weakListener.OnEvent;
 

Then defining the event handler method an assign it to the OnEventAction delegate.

// define event handler code.
weakListener.OnEventAction = (instance, source, e) =>
{
    // event handling code
    instance.longLivingInstance_PropertyChanged(instance, e);
};

The event handler method must be static. Why this is important? The OnEventAction is a delegate to a method and this method defines the action when the event is raising. It is important that the delegate to this method has no Target set and this is the case when the target method is static.

clip_image002[5]

How it works with lambda statement? When writing a lambda statement, the compiler generates an anonymous method which is static:

[CompilerGenerated]
private static void <Listener_Loaded>b__0(Listener instance, object source, PropertyChangedEventArgs e)
{
    instance.longLivingInstance_PropertyChanged(instance, e);
}
 

But when one accesses any instance member inside the OnEventAction method then the compiler generates an instance method instead of a static.

[CompilerGenerated]
private void <Listener_Loaded>b__0(Listener instance, object source, PropertyChangedEventArgs e)
{
    this.longLivingInstance_PropertyChanged(instance, e);
} 
 

Because of this small difference the delegate has now a reference back to the listener and the memory leak still remains (only with a little more code…)

clip_image002[7]

The debugger shows it clearly.

clip_image004[6]

This makes the detail whether the WeakEventListener works correctly or not. Therefore, I extended the original WeakEventListener code to prevent this mistake. The OnEventAction property checks in its setter whether the assigned delegate pointing to a static method or not.

 
public Action<TInstance, object, TEventArgs> OnEventAction
{
    get { return _onEventAction; }
    set
    {
        if (value != null && !value.Method.IsStatic)
            throw new ArgumentException("OnEventAction method must be static otherwise the " +
                                        "event WeakEventListner class does not prevent memory leaks.");

        _onEventAction = value;
    }
}
 
Lastly, the weakListener needs an action to unregister the event handler.
 
// define action to detach the handler
weakListener.OnDetachAction = (listener, source) =>
{
    // unregister handler
    source.PropertyChanged -= listener.OnEvent;
};
 

The OnDetachAction is called when one calls the Detach method on the weakListener or when the listener isn’t alive anymore and the event-source is raising events. Here I did another small change in the source code: the OnDetachAction provides the event-source instance as the second parameter. With this the WeakEventListener instance can guarantee to unregister the source-event even though all others had released the EventSource. In other words the WeakEventListener instance can unregisters itself from the EventSource. To get a safety implementation of this rule the OnDetachAction property does the same static-method check as the OnEventAction property.

Summary

The Silverlight Toolkit contains an excellent implementation of a WeakEventListener. It doesn’t use reflection or dynamic assembly generation, so you don’t bother with performance or security problems. The only problem I see is that it can be used incorrectly. Therefore, I slightly modified the WeakEventListener with some runtime checks to prevent these mistakes.

In a next post I’m going to document an additional wrapper-pattern I’ve build around the WeakEventListener. The wrapper is designed as an easy to use weak event listener for a specify event source such as the common PropertyChanged and CollectionChanged events from the INotifyPropertyChanged and INotifyCollectionChanged interfaces.

TestViewModel longLivingInstance = new TestViewModel();
NotifyPropertyChangedListener listener = new NotifyPropertyChangedListener(longLivingInstance);
listener.PropertyChanged += new PropertyChangedEventHandler(listener_PropertyChanged);

 

And I want to explain why for us the weak event pattern is such much important when writing custom controls or making async service calls.

Source Code

Here you can find the full source code.

image

Modified WeakEventListener class (original is in Silverlight Toolkit):

//-----------------------------------------------------------------------
//  This source is a slightly modified version from the WeakEventListner
//  in the Silverlight Toolkit Source (www.codeplex.com/Silverlight)
//---------------------------Original Source-----------------------------
// <copyright company="Microsoft">
//      (c) Copyright Microsoft Corporation.
//      This source is subject to the Microsoft Public License (Ms-PL).
//      Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
//      All other rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System.Diagnostics.CodeAnalysis;
using System.Diagnostics;

namespace System.Windows.Controls
{
    /// <summary>
    /// Implements a weak event listener that allows the owner to be garbage
    /// collected if its only remaining link is an event handler.
    /// </summary>
    /// <typeparam name="TInstance">Type of rootInstance listening for the event.</typeparam>
    /// <typeparam name="TSource">Type of source for the event.</typeparam>
    /// <typeparam name="TEventArgs">Type of event arguments for the event.</typeparam>
    internal class WeakEventListener<TInstance, TSource, TEventArgs> where TInstance : class
    {

        #region Fields

        /// <summary>
        /// WeakReference to the rootInstance listening for the event.
        /// </summary>
        private WeakReference _weakInstance;

        /// <summary>
        /// To hold a reference to source object. With this instance the WeakEventListener 
        /// can guarantee that the handler get unregistered when listener is released.
        /// </summary>
        private WeakReference _weakSource;

        /// <summary>
        /// Delegate to the method to call when the event fires.
        /// </summary>
        private Action<TInstance, object, TEventArgs> _onEventAction;

        /// <summary>
        /// Delegate to the method to call when detaching from the event.
        /// </summary>
        private Action<WeakEventListener<TInstance, TSource, TEventArgs>, TSource> _onDetachAction;

        #endregion

        #region Ctor

        /// <summary>
        /// Initializes a new instances of the WeakEventListener class.
        /// </summary>
        /// <param name="rootInstance">Instance subscribing to the event.</param>
        public WeakEventListener(TInstance instance, TSource source)
        {
            if (null == instance)
            {
                throw new ArgumentNullException("instance");
            }

            if (source == null)
                throw new ArgumentNullException("source");

            _weakInstance = new WeakReference(instance);
            _weakSource = new WeakReference(source);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the method to call when the event fires.
        /// </summary>
        public Action<TInstance, object, TEventArgs> OnEventAction
        {
            get { return _onEventAction; }
            set
            {
                // CHANGED: NEVER REMOVE THIS CHECK. IT CAN CAUSE A MEMORY LEAK.
                if (value != null && !value.Method.IsStatic)
                    throw new ArgumentException("OnEventAction method must be static "+
                              "otherwise the event WeakEventListner class does not prevent memory leaks.");

                _onEventAction = value;
            }
        }

        /// <summary>
        /// Gets or sets the method to call when detaching from the event.
        /// </summary>
        internal Action<WeakEventListener<TInstance, TSource, TEventArgs>, TSource> OnDetachAction
        {
            get { return _onDetachAction; }
            set
            {

                // CHANGED: NEVER REMOVE THIS CHECK. IT CAN CAUSE A MEMORY LEAK.
                if (value != null && !value.Method.IsStatic)
                    throw new ArgumentException("OnDetachAction method must be static otherwise " +
                               "the event WeakEventListner cannot guarantee to unregister the handler.");

                _onDetachAction = value;
            }
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Handler for the subscribed event calls OnEventAction to handle it.
        /// </summary>
        /// <param name="source">Event source.</param>
        /// <param name="eventArgs">Event arguments.</param>
        public void OnEvent(object source, TEventArgs eventArgs)
        {
            TInstance target = (TInstance)_weakInstance.Target;
            if (null != target)
            {
                // Call registered action
                if (null != OnEventAction)
                {
                    OnEventAction(target, source, eventArgs);
                }
            }
            else
            {
                // Detach from event
                Detach();
            }
        }

        /// <summary>
        /// Detaches from the subscribed event.
        /// </summary>
        public void Detach()
        {
            // CHANGED: 30.03.2010


TSource source = (TSource)_weakSource.Target;
            if (null != OnDetachAction && source != null)
            {
                // CHANGED: Passing the source instance also, because of static event handlers
                OnDetachAction(this, source);
                OnDetachAction = null;
            }
        }

        #endregion
    }
}
Tagged ,