views:

550

answers:

1

I have a screen with a ListBox of items. The item template contains an expander control with some of the data in the header and some of the data in the content part of the expander.

The data template for the ListBox ItemTemplate is similar to this:

<DataTemplate x:Key="MyTypeTemplate" DataType="{x:Type MyType}">
  <Expander DataContext="{Binding}">
    <Expander.Header>
      <Canvas>
        <TextBox Text="{Binding MyProperty}"/>
      </Canvas>
    </Expander.Header>
    <Canvas>
      <TextBox Text={Binding MyDetailedProperty}"/>
    </Canvas>
  </Expander>
</DataTemplate>

Whenever these properties change, either 'MyProperty' or 'MyDetailedProperty' changes, the expander control collapsed. I believe that is has something to do with the Expander item getting recreated when the data changes.

As an additional data item, the list being bound to the listbox implements IBindingList as it comes from a library created for .NET 2.0. I cannot recreate the list using ObservableCollection due to time constraints

A: 

I ended up wrapping my model objects in a view object that adds an IsExpandable property that I could bind to the Expanded IsExpanded property and then exposed the data.

This is not a general purpose solution but it solves my immediate problem. The possible issues that I see that I haven't explored are whether the PropertyChanged and ListChanged event attaches cause memory leak issues with my UI objects, but in my situation each object should only be created once.

Also, events beyond Add and Remove in the collection change are not supported, but in my case I'm not firing anything else so it is safe for me to ignore them.

public class ExpandableItem<T> : INotifyPropertyChanged 
    where T: INotifyPropertyChanged
{
    private bool m_isExpanded;
    private readonly T m_data;

    public ExpandableItem(T data)
    {
        m_data = data;

        m_data.PropertyChanged += 
          delegate 
          { 
            PropertyChanged(this, new PropertyChangedEventArgs("Data")); 
          };
    }

    public bool IsExpanded
    {
        get { return m_isExpanded; }
        set
        {
            if (value != m_isExpanded)
            {
                m_isExpanded = value;
                PropertyChanged(this, new PropertyChangedEventArgs("IsExpanded"));
            }
        }
    }

    public T Data
    {
        get
        {
            return m_data;
        }

    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
} 

public class ExpandableList<TObject,TList> :
  ObservableCollection<ExpandableItem<TObject>>
  where TList : ObservableCollection<TObject>
  where TObject : INotifyPropertyChanged
{
    readonly TList m_list;

    public ExpandableList(TList list) 
        : base(list.Select(obj=>new ExpandableItem<TObject>(obj)))
    {
        list.CollectionChanged += OnListChanged;
        m_list = list;
    }

    public TList Data { get { return m_list; } }

    private void OnListChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            Insert(e.NewStartingIndex, e.NewItems[0]);
        }
        if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            RemoveAt(e.OldStartingIndex);
        }
    }
}
Steve Mitcham