March 10, 2006
07:14 PM

I just tried to do this:

<TreeView SelectedValue="{Binding Path=Selected, Mode=OneWayToSource}" />
<ItemsControl ItemsSource="{Binding FlattenedTree.Items}" />

But as usual, it didn't work. "The property 'SelectedValue' is read-only and cannot be set from markup." Yeah, plus, it's not a Dependency Property anyway. But, I'm not trying to set it, just get influnce from it back into my model. I used a similar technique in the past to send a search string from the Text property of a TextBox into my model to initiate a search.

The code in the ViewModel looked like this:

private object selected;
public object Selected
{
    set
    {
        this.selected = value;
        Changed("FlattenedTree");
    }
}

public FlattenedTree FlattenedTree
{
    get 
    { 
        if (selected == null)
            return null;
        return new FlattenedTree(selected); 
    }
}

Where FlattenedTree is another property in my ViewModel bound in the View and Changed just triggers an event over the INotifyPropertyChanged interface.

Of course, after writing this, compiling, getting that error, blaming the WPF team for not including enough dependency properties, I realised I'm still not thinking in WPF. So I refactored. Instead of pusing the selected item back into the model to convert it and send it out again - lets just convert it!

<TreeView Name="TreeView" ItemsSource="{Binding Root.Children}" />
<Grid DataContext="{Binding ElementName=TreeView, Path=SelectedValue, 
    Converter={x:Static local:FlattenedTreeConverter.Instance}}">
    <ItemsControl ItemsSource="{Binding Items}" />
</Grid>
public class FlattenedTreeConverter : System.Windows.Data.IValueConverter
{
    public static FlattenedTreeConverter Instance
    {
        get { return new FlattenedTreeConverter(); }
    }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null)
            return null;
        return new FlattenedTree(value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new System.NotImplementedException();
    }
}

I'm much happier with this. Note I'm setting the DataContext on an enclosing Grid because other controls will need access to the FlattenedTree object. Hooking the converter up via the static instance isn't so great, especially seeing as I'm not caching it. But, for some reason I couldn't instantiate it as a resource.

I imagine I can simplify this further by synchronising on the current item of a CollectionView around Root.Children. That should avoid having to name and reference across elements. Update: Sorry, I forgot I was dealing with a tree of data, not a list, no CollectionViews there.

© Douglas Stockwell 2007
Creative Commons License Unless otherwise specified all "source code" examples are available for use under the Creative Commons Attribution-Noncommercial 3.0 License. Please contact me if you would like more flexible licensing terms.
Messenger Presence