My blog about software development on the Microsoft® stack.

Using behaviours to bind to read-only properties in MVVM

If you have ever tried to for example bind the SelectedItem property of a TreeView control or the SelectedDates property of a Calendar control to some source property of a view model, you know that these properties are read-only, i.e. they don’t have a publicly accessible setter.

This post provides two different code samples on how you can use something called attached behaviours to be able to synchronize a source property of a view model class with a custom dependency property that acts as replacement for an existing read-only dependency property of some user interface control.

Example 1: TreeView.SelectedItem

Let’s assume that you are developing a WPF application using the Model-View-View Model (MVVM) design pattern and you want do display the details of some item in a ContentPresenter once it gets selected in a TreeView control. To illustrate this scenario, consider the following sample view model class that creates and exposes a collection of FileExplorerItem objects that represent folders and files of a given root folder on the disk:

public class ViewModel : INotifyPropertyChanged
{
    private const string rootDirPath = @"C:\Users\magnus\Documents\Blog\Countries\";
    public ViewModel()
    {
        System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(rootDirPath);
        Directory rootDirectory = CreateDirectory(dirInfo);
        this.Items = new List<FileExplorerItem>() { rootDirectory };
    }

    private Directory CreateDirectory(System.IO.DirectoryInfo dirInfo)
    {
        Directory directory = new Directory()
        {
            Name = dirInfo.Name,
            Path = dirInfo.FullName,
            SubItems = new List<FileExplorerItem>(),
        };

        //Add each file in the current directory
        foreach (System.IO.FileInfo fi in dirInfo.GetFiles())
        {
            File file = new File()
            {
                Name = fi.Name,
		Path = dirInfo.FullName
            };
            directory.SubItems.Add(file);
        }

        // Add each subdirectory using recursion 
        foreach (System.IO.DirectoryInfo subDirInfo in dirInfo.GetDirectories())
        {
            directory.SubItems.Add(CreateDirectory(subDirInfo));
        }

        return directory;
    }

    public IList<FileExplorerItem> Items { get; set; }

    private FileExplorerItem _selectedItem;
    public FileExplorerItem SelectedItem
    {
        get { return _selectedItem; }
        set { _selectedItem = value; NotifyPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class FileExplorerItem
{
    public string Name { get; set; }
    public string Path { get; set; }
}

public class Directory : FileExplorerItem
{
    public IList<FileExplorerItem> SubItems { get; set; }
}

public class File : FileExplorerItem
{
    public string Content
    {
        get
        {
            //read file content (IsAsync=True) ...
            string path = string.Format("{0}\\{1}", this.Path, this.Name);
            if (System.IO.File.Exists(path))
                return System.IO.File.ReadAllText(path);

            return string.Empty;
        }
    }
}

The view model has a property named SelectedItem that is supposed to keep track of the currently selected FileExplorerItem object in the view, whose markup consists of a TreeView control that is bound to the Items collection of the view model and a ContentPresenter that is bound to this SelectedItem property of the same:

<Window x:Class="Mm.Behaviours.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Mm.Behaviours"
        Title="blog.magnusmontin.net" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TreeView ItemsSource="{Binding Items}"
                  Grid.Column="0">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type local:Directory}" 
                                          ItemsSource="{Binding SubItems}">
                    <TextBlock Text="{Binding Name}"/>
                </HierarchicalDataTemplate>
                <DataTemplate DataType="{x:Type local:File}">
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </TreeView.Resources>
        </TreeView>
        <!-- Present details of the currently selected item in a ContentPresenter -->
        <ContentPresenter Content="{Binding SelectedItem}"
                          Grid.Column="1">
            <ContentPresenter.Resources>
                <DataTemplate DataType="{x:Type local:Directory}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Text="Folder: " />
                        <TextBlock Text="{Binding Name}" FontWeight="Bold" Grid.Column="1"/>
                        <TextBlock Text="Number of items: " Grid.Row="1" />
                        <TextBlock Text="{Binding SubItems.Count}" FontWeight="Bold" 
                                   Grid.Row="1" Grid.Column="1"/>
                    </Grid>
                </DataTemplate>
                <DataTemplate DataType="{x:Type local:File}">
                    <StackPanel>
                        <TextBlock>
                            <Run Text="File: " FontWeight="Bold"/>
                            <Run Text="{Binding Name}" />
                        </TextBlock>
                        <TextBlock Text="{Binding Content, IsAsync=True}" />
                    </StackPanel>
                </DataTemplate>
            </ContentPresenter.Resources>
        </ContentPresenter>
    </Grid>
</Window>

If you simply try to bind the SelectedItem property of the TreeView control to the SelectedItem source property of the view model in XAML using the ordinary binding markup extension syntax, Visual Studio will put a squiggly line under the property in the XAML view and tell you that the “‘SelectedItem’ property is read-only and cannot be set from markup”.

Visual Studio 2013
So how can you solve this issue? How would you go about to set the SelectedItem property of the view model once the user selects a FileExplorerItem in the TreeView without breaking the MVVM pattern?

In this particular example, you could add a Boolean property to the FileExplorerItem class and define an ItemContainerStyle that binds the IsSelected property of an individual TreeViewItem to this property and then implement the SelectedItem property of the view model to return the first FileExplorerItem object in the Items collection that has this property set to true:

public class FileExplorerItem : INotifyPropertyChanged
{
    public string Name { get; set; }
    public string Path { get; set; }

    //new property:
    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set { _isSelected = value; NotifyPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
<TreeView ItemsSource="{Binding Items}"
           Grid.Column="0">
    <TreeView.ItemContainerStyle>
        <Style TargetType="TreeViewItem">
            <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.Resources>
        ...
    </TreeView.Resources>
</TreeView>
public class ViewModel : INotifyPropertyChanged
{
    ...
    private Directory CreateDirectory(System.IO.DirectoryInfo dirInfo)
    {
        Directory directory = new Directory()
        {
            Name = dirInfo.Name,
            Path = dirInfo.FullName,
            SubItems = new List<FileExplorerItem>(),
        };
        directory.PropertyChanged += item_PropertyChanged;

        //Add each file in the current directory
        foreach (System.IO.FileInfo fi in dirInfo.GetFiles())
        {
            File file = new File()
            {
                Name = fi.Name,
                Path = dirInfo.FullName
            };
            file.PropertyChanged += item_PropertyChanged;
            directory.SubItems.Add(file);
        }


        // Add each subdirectory using recursion 
        foreach (System.IO.DirectoryInfo subDirInfo in dirInfo.GetDirectories())
        {
            directory.SubItems.Add(CreateDirectory(subDirInfo));
        }

        return directory;
    }

    private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        FileExplorerItem item = sender as FileExplorerItem;
        if(item != null && item.IsSelected)
            NotifyPropertyChanged("SelectedItem");
    }

    ...

    public FileExplorerItem SelectedItem
    {
        get
        {
            return GetSelectedItem(this.Items);
        }
    }

    private static FileExplorerItem GetSelectedItem(IEnumerable<FileExplorerItem> items)
    {
        //top-level items:
        FileExplorerItem item = items.FirstOrDefault(i => i.IsSelected);
        if (item == null)
        {
            //sub-level items:
            IEnumerable<FileExplorerItem> subItems = items.OfType<Directory>().SelectMany(d => d.SubItems);
            if(items.Any())
                item = GetSelectedItem(subItems);
        }
        return item;
    }
    ...
}

Note however that for this to work as expected, you need raise the PropertyChanged event for the SelectedItem property of the view model whenever the IsSelected property of a FileExplorerItem object in the Items collection changes. You also need to use recursion to iterate through the collection of FileExplorerItem objects to find the particular item that is selected. This may not be an acceptable or particularly good solution. Also, in some real-world cases it might not even be possible to add an additional property to a class for one reason or another.

To solve issues like this, you can instead implement a custom behaviour. A behaviour is basically a piece of code that can be attached to some user element in the XAML markup of a view through attached properties and add functionality to this element.

There are basically two different ways of implementing these kind of behaviours in WPF, commonly referred to as attached behaviours and Blend behaviours. If you are familiar with dependency properties and attached properties, an attached behaviour is simply an attached property with a PropertyChangedCallback change handler attached to it that performs some action on or extends the DependencyObject to which it is attached when the value of the dependency property changes. You typically define an attached behaviour as a static class with a set of static methods that get and set the value of the dependency property and perform the desired logic as a result of the callback being invoked.

A Blend behaviour provides a better way of encapsulating the functionality of a behaviour compared to an ordinary attached behaviour. They are also more design friendly easily as they can be easily attached to visual elements in the UI via drag-drop functionality in the Expression Blend or Blend for Visual Studio tool.

You define a Blend behaviour by creating a non-static class that derives from the System.Windows.Interactivity.Behavior<T> class. Note that this abstract base class doesn’t ship with the .NET Framework but it is part of the Microsoft Expression Blend SDK that can be downloaded from Microsoft’s official website here. You will need to reference the System.Windows.Interactivity.dll from your application or class library.

Below is an example of a Blend behaviour that defines its own SelectedItem dependency property to be bound to the source property of the view model. Note that the generic type parameter of the Behavior<T> base class specifies the type of element that the behaviour can be attached to, in this case System.Windows.Controls.TreeView.

The custom behaviour class also overrides the OnAttached and OnDetaching methods of the non-generic System.Windows.Interactivity.Behavior class, which is the abstract base class for the generic one, to hook up an event handler for the SelectedItemChanged event of the TreeView object that is returned by the protected AssociatedObject property of the Behavior<T> class. The event handler will set the SelectedItem dependency property of the behaviour to the item that was selected in the TreeView control by the user.

public class TreeViewSelectedItemBlendBehavior : Behavior<TreeView>
{
    //dependency property
    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem", typeof(object),
        typeof(TreeViewSelectedItemBlendBehavior),
        new FrameworkPropertyMetadata(null) { BindsTwoWayByDefault = true });

    //property wrapper
    public object SelectedItem
    {
        get { return (object)GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        if (this.AssociatedObject != null)
            this.AssociatedObject.SelectedItemChanged -= OnTreeViewSelectedItemChanged;
    }

    private void OnTreeViewSelectedItemChanged(object sender, 
        RoutedPropertyChangedEventArgs<object> e)
    {
        this.SelectedItem = e.NewValue;
    }
}

You can then easily add the above behaviour to the TreeView control as shown in the markup below and the SelectedItem property of the view model will then be set to the currently selected FileExplorerItem object whenever the user selects a new node in the TreeView control.

<TreeView xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
          ItemsSource="{Binding Items}"
          Grid.Column="0">
    <i:Interaction.Behaviors>
        <local:TreeViewSelectedItemBlendBehavior
                    SelectedItem="{Binding SelectedItem}" />
    </i:Interaction.Behaviors>
    <TreeView.Resources>
        ...
    </TreeView.Resources>
</TreeView>

TreeView
If you want the behaviour to be able handle the binding between the view model and the view in the opposite direction, i.e. when you set the SelectedItem property in the view model class, you need to add some more code to it.

You can specify a PropertyChangedCallback to be invoked whenever the value of the dependency property changes when you register the dependency property using the DependencyProperty.Register method. In the callback method, you can then get the visual TreeViewItem object for the data item that was set as the value of the SelectedItem property in the view model, and set its IsSelected property to true. There is a great article on how to find a TreeViewItem in a TreeView available on MSDN here. The provided GetTreeViewItem method will be able to find and realize the corresponding TreeViewItem of the data object even if it has been virtualized away provided that you use the custom VirtualizingStackPanel as the ItemsPanel for both the TreeView control and the individual TreeViewItem objects. Also note that a reference to the TreeView control is stored in a variable called _treeView in the OnAttached in the modified sample behaviour class below. This reference is then passed to the GetTreeViewItem method from the callback method:

public class TreeViewSelectedItemBlendBehavior : Behavior<TreeView>
{
    //dependency property
    public static readonly DependencyProperty SelectedItemProperty =
        DependencyProperty.Register("SelectedItem", typeof(object),
        typeof(TreeViewSelectedItemBlendBehavior),
        new FrameworkPropertyMetadata(null, OnSelectedItemChanged) { BindsTwoWayByDefault = true });

    //.NET property wrapper
    ...

    private static TreeView _treeView;
    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.SelectedItemChanged += OnTreeViewSelectedItemChanged;
        _treeView = this.AssociatedObject;
    }

    protected override void OnDetaching(){ ... }

    private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    { ... }

    private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        //find corresponding item in the TreeView and select it
        object dataItem = e.NewValue; //a FileExplorerItem object
        TreeViewItem tvi = GetTreeViewItem(_treeView, dataItem);
        if (tvi != null)
            tvi.SetValue(TreeViewItem.IsSelectedProperty, true);
    }

    private static TreeViewItem GetTreeViewItem(ItemsControl container, object item)
    {
        /* Refer to http://msdn.microsoft.com/en-us/library/ff407130(v=vs.110).aspx 
         * for implementation details of this method */
    }

    private static T FindVisualChild<T>(Visual visual) where T : Visual
    {
        /* Refer to http://msdn.microsoft.com/en-us/library/ff407130(v=vs.110).aspx 
         * for implementation details of this method */
    }
}

public ViewModel()
{
    System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(rootDirPath);
    Directory rootDirectory = CreateDirectory(dirInfo);
    this.Items = new List<FileExplorerItem>() { rootDirectory };

    //select the second sub item of the root directory:
    this.SelectedItem = rootDirectory.SubItems[1];
}

TreeView

Example 2: Calendar.SelectedDates

You can two-way bind the SelectedDates property of a Calendar control using the same approach: create a class that inherits from Behavior, register a dependency property of type System.Collections.ObjectModel.ObservableCollection<System.DateTime>, hook up the SelectedDatesChanged event of the Calendar control by overriding the OnAttached method of the Behavior class, unsubscribe from the same event by overriding the OnDetaching method of the Behavior class, and implement the event handler to add and remove DateTime objects from the dependency property:

public class CalendarSelectedDatesBlendBehavior : Behavior<Calendar>
{
    //dependency property
    public static readonly DependencyProperty SelectedDatesProperty =
        DependencyProperty.Register("SelectedDates", typeof(ObservableCollection<DateTime>),
        typeof(CalendarSelectedDatesBlendBehavior),
        new FrameworkPropertyMetadata(null) { BindsTwoWayByDefault = true });

    //.NET property wrapper
    public ObservableCollection<DateTime> SelectedDates
    {
        get { return (ObservableCollection<DateTime>)GetValue(SelectedDatesProperty); }
        set { SetValue(SelectedDatesProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.SelectedDatesChanged += OnCalendarSelectedDatesChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        if (this.AssociatedObject != null)
            this.AssociatedObject.SelectedDatesChanged -= OnCalendarSelectedDatesChanged;
    }


    private void OnCalendarSelectedDatesChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems != null && e.AddedItems.Count > 0)
        {
            IEnumerable<DateTime> addedDates = e.AddedItems.OfType<DateTime>();
            foreach (DateTime dt in addedDates)
                this.SelectedDates.Add(dt);
        }

        if (e.RemovedItems != null && e.RemovedItems.Count > 0)
        {
            IEnumerable<DateTime> removedDates = e.RemovedItems.OfType<DateTime>();
            foreach (DateTime dt in removedDates)
                this.SelectedDates.Remove(dt);
        }
    }
}

Below is a sample view that uses the above behaviour to bind to the SelectedDates property of a view model. Below the Calendar control, there is an ItemsControl that displays the DateTime objects as they are being added to the source collection when the user selects dates in the control by clicking on them with the mouse while pressing down the control key on the keyboard:

public class CalendarViewModel
{
    public CalendarViewModel()
    {
        this.SelectedDates = new ObservableCollection<DateTime>();
    }

    public ObservableCollection<DateTime> SelectedDates
    {
        get;
        set;
    }
}
<Window x:Class="Mm.Behaviours.CalendarWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:local="clr-namespace:Mm.Behaviours"
        Title="blog.magnusmontin.net" Height="300" Width="300">
    <StackPanel>
        <Calendar x:Name="calendar"
                  SelectionMode="MultipleRange">
            <i:Interaction.Behaviors>
                <local:CalendarSelectedDatesBlendBehavior
                    SelectedDates="{Binding SelectedDates}" />
            </i:Interaction.Behaviors>
        </Calendar>
        <TextBlock Text="Selected dates: " FontWeight="Bold" />
        <ItemsControl ItemsSource="{Binding SelectedDates}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <!-- TextBlock for displaying a DateTime object from the 
                    SelectedDates property of the view model -->
                    <TextBlock Text="{Binding Path=., StringFormat=d}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </StackPanel>
</Window>

Calendar
To be able to set selected dates dynamically in code and have this reflected in the Calendar control in the view, the implementation differs a bit compared to the TreeViewSelectedItemBlendBehavior class.

As the type of the SelectedDates dependency object of the behaviour class is anObservableCollection<DateTime>, the PropertyChangedCallback delegate will only be invoked when the source property is being set to some entirely new collection. For the Calendar control to be able to display dates that are being added or removed from an initial collection at runtime as selected and unselected respectively, you also need to handle the CollectionChanged event of the source collection.

Just like before, the sample code below registers a PropertyChangedCallback for the dependency property. When this callback is invoked as result of the dependency property being set to the ObservableCollection<DateTime> of the view model, the dates in the source collection will be added to the calendar control’s SelectedDates collection. Note that a variable called _collectionSetFromSource is used and set to prevent the OnCalendarSelectedDatesChanged method from trying to add the DateTime objects to the very same source collection that they came from again. Remember that the OnCalendarSelectedDatesChanged method handles the SelectedDatesChanged event of the Calendar control and this event will be raised whenever you add a new DateTime object to the Calendar’s SelectedDates collection, just like it is raised when the user selects a date by clicking on it with the mouse.

public class CalendarSelectedDatesBlendBehavior : Behavior<Calendar>
{
    //dependency property
    public static readonly DependencyProperty SelectedDatesProperty =
        DependencyProperty.Register("SelectedDates", typeof(ObservableCollection<DateTime>),
        typeof(CalendarSelectedDatesBlendBehavior),
        new FrameworkPropertyMetadata(null, OnSelectedDatesChanged) { BindsTwoWayByDefault = true });

    //.NET property wrapper
    ...

    private static Calendar _calendar;
    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.SelectedDatesChanged += OnCalendarSelectedDatesChanged;
        _calendar = this.AssociatedObject;
    }

    protected override void OnDetaching(){ ... }


    private void OnCalendarSelectedDatesChanged(object sender, SelectionChangedEventArgs e)
    {
        if (!_collectionSetFromSource)
        {
            if (e.AddedItems != null && e.AddedItems.Count > 0)
            {
                IEnumerable<DateTime> addedDates = e.AddedItems.OfType<DateTime>();
                foreach (DateTime dt in addedDates)
                    this.SelectedDates.Add(dt);
            }

            if (e.RemovedItems != null && e.RemovedItems.Count > 0)
            {
                IEnumerable<DateTime> removedDates = e.RemovedItems.OfType<DateTime>();
                foreach (DateTime dt in removedDates)
                    this.SelectedDates.Remove(dt);
            }
        }
    }

    private static bool _collectionSetFromSource;
    private static ObservableCollection<DateTime> _sourceCollection;
    private static void OnSelectedDatesChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        //invoked when the source property, 
        //i.e. the entire ObservableCollection<DateTime> property is being set...
        _sourceCollection = e.NewValue as ObservableCollection<DateTime>;
        if (_sourceCollection != null)
        {
            _sourceCollection.CollectionChanged += dates_CollectionChanged;
            foreach (DateTime dt in _sourceCollection)
            {
                _collectionSetFromSource = true;
                _calendar.SelectedDates.Add(dt);
                _collectionSetFromSource = false;
            }
        }
    }
    ...
}

Note that the CollectionChanged event of the source collection is hooked up in the OnSelectedDatesChanged callback. The event handler adds or removes a date from the SelectedDates collection of the Calendar control depending on the action of the NotifyCollectionChangedEventArgs:

private static void dates_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    switch(e.Action)
    {
        case NotifyCollectionChangedAction.Add:
            if (e.NewItems != null && e.NewItems.Count > 0)
            {
                IEnumerable<DateTime> addedDates = e.NewItems.OfType<DateTime>();
                foreach(DateTime dt in addedDates)
                {
                    if (!_calendar.SelectedDates.Contains(dt))
                    {
                        _collectionSetFromSource = true;
                        _calendar.SelectedDates.Add(dt);
                        _collectionSetFromSource = false;
                    }
                }
            }
            break;
        case NotifyCollectionChangedAction.Remove:
            if (e.OldItems != null && e.OldItems.Count > 0)
            {
                IEnumerable<DateTime> removedDates = e.OldItems.OfType<DateTime>();
                foreach (DateTime dt in removedDates)
                {
                    if (_calendar.SelectedDates.Contains(dt))
                    {
                        _collectionSetFromSource = true;
                        _calendar.SelectedDates.Remove(dt);
                        _collectionSetFromSource = false;
                    }
                }
            }
            break;
    }
}

Finally, also note that you should keep a reference to the source collection in order for you to be able to unsubscribe from its CollectionChanged event once the behaviour is detached:

protected override void OnDetaching()
{
    base.OnDetaching();
    if (this.AssociatedObject != null)
        this.AssociatedObject.SelectedDatesChanged -= OnCalendarSelectedDatesChanged;
    if (_sourceCollection != null)
        _sourceCollection.CollectionChanged -= dates_CollectionChanged;
}

This is especially important if the view model object lives longer than the view for the control to which the behaviour is attached. Otherwise, the view model will prevent the view object from being garbage collected and your application will end up with a memory leak.


6 Comments on “Using behaviours to bind to read-only properties in MVVM”

  1. Harvey says:

    Very interesting article. Is there anyway you can zip and submit the source files

  2. Adsi says:

    good one, but where is the source code ?

  3. Hauke says:

    Good work. I searched many sites on this topic, and I find your post most conclusive. Great help, thanx

  4. hi,

    I’ve used this calendar behavior in my app (set the dates from a viewmodel/binding) but the only problem is have left is that the user is still able to select other dates, which is something i would like to turn off. I hoped i could do it by setting the binding to oneway but that didn’t have the desired effect, also capturing the mousedown event and voiding it didn’t do what i want. You have any inspiration on this?
    gr,S.

  5. me says:

    Cannot create a TwoWay binding on a read-only property.

  6. vinod says:

    Nice work. i have searched lots of article but none of them has properly explained.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s