views:

279

answers:

2

I made a CollectionToStringConverter which can convert any IList into a comma-delimited string (e.g. "Item1, Item2, Item3").

I use it like this:

<TextBlock Text="{Binding Items, 
                  Converter={StaticResource CollectionToStringConverter}}" />

The above works, but only once when I load the UI. Items is an ObservableCollection. The text block does not update, and the converter does not get called when I add or remove from Items.

Any idea what's missing to make this work?

+2  A: 

The binding is to the property yielding the collection. It will come into effect whenever the collection instance itself changes, not when items in the collection are changed.

There are quite a few ways to achieve the behavior you want, including:

1) Bind an ItemsControl to the collection and configure the ItemTemplate to output the text preceded by a comma if it's not the last item in the collection. Something like:

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemTemplate>
        <TextBlock>
            <TextBlock Visibility="{Binding RelativeSource={RelativeSource PreviousData}, Converter={StaticResource PreviousDataConverter}}" Text=", "/>
            <TextBlock Text="{Binding .}"/>
        </TextBlock>
    </ItemsControl.ItemTemplate>    
</ItemsControl>

2) Write code in your code-behind to watch the collection for changes and update a separate property that concatenates the items into a single string. Something like:

public ctor()
{
    _items = new ObservableCollection<string>();

    _items.CollectionChanged += delegate
    {
        UpdateDisplayString();
    };
}

private void UpdateDisplayString()
{
    var sb = new StringBuilder();

    //do concatentation

    DisplayString = sb.ToString();
}

3) Write your own ObservableCollection<T> subclass that maintains a separate concatenated string similar to #2.

HTH, Kent

Kent Boogaart
I had actually started off using the ItemsTemplate approach, but during a code review, we thought it would be simpler to do it through a multi binding converter, thinking the binding would get updated when the observed collection changed. I'll revert back to that method :) Thanks!
Anthony Brien
+1  A: 

Converter will get called only when the Property changes. In this case the 'Items' value is not changing. When you add or remove new items in to the collection the binding part is not aware of that.

You can extend the ObservableCollection and add a new String property in it.Remember to update that property in your CollectionChanged event handler.

Here is the Implementation

public class SpecialCollection : ObservableCollection<string>, INotifyPropertyChanged
{

    public string CollectionString
    {
        get { return _CollectionString; }
        set { 
            _CollectionString = value;
            FirePropertyChangedNotification("CollectionString");
        }
    }


    protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        string str = "";

        foreach (string s in this)
        {
            str += s+",";
        }

        CollectionString = str;

        base.OnCollectionChanged(e);
    }

    private void FirePropertyChangedNotification(string propName)
    {
        if (PropertyChangedEvent != null)
            PropertyChangedEvent(this,
               new PropertyChangedEventArgs(propName));
    }   

    private string _CollectionString;
    public event PropertyChangedEventHandler PropertyChangedEvent;   

}

And your XAML will be like

<TextBlock DataContext={Binding specialItems} Text="{Binding CollectionString}" />
Jobi Joy