Share via


Binding to a collection of strings in Avalon

Initially, I tried the obvious and used ObservableCollection<string> but although I had specified the following XAML:

<

StackPanel.Resources>
<DataTemplate x:Key="ListBoxItemStyle">
<StackPanel Orientation="Horizontal">
<Button Margin="10,0,10,0" Click="DeleteDirection" Tag="{Binding}">Delete</Button>
<TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
</StackPanel>
</DataTemplate>
</StackPanel.Resources>

<

ItemsControl Name="DirectionsList" ItemTemplate="{StaticResource ListBoxItemStyle}" />

it would not udate the strings! Displaying them was fine but I could not make the binding infrastructure change the strings inside the ObservableCollection<string> instance.

Then I receive the following e-mail from the Avalon team:

This is expected. Look at it from the point of view of the binding:

<TextBoxText = "{Binding Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>

When you type a new string into the TextBox, where do you expect the binding to store it? There's no object with a property it can set, because there's no binding path. So perhaps you expect it to replace it's "root" object. But what does that mean? The root object comes from the inherited DataContext, so at the minimum this implies finding the ancestor where DataContext was defined and resetting ancestor.DataContext. But this isn't right either - we'd really need to go back in time and find out how the ancestor acquired its DataContext, and follow the trail back to the original source of the object. Time travel is not supported in Avalon, alas. You and I can look at your example, run the clock forward, and determine where the value came from in this particular instance. But the poor binding cannot do it, since it doesn't know where its DataContext came from, or which collection the item belongs to, or where it is in that collection.

So I created the following class:

public class StringWrapperForBinding
{
public StringWrapperForBinding() : this(String.Empty)
{
}
public StringWrapperForBinding(string s)
{
wrappedString = s;
}
public string WrappedString
{
get { return wrappedString; }
set { wrappedString = value; }
}
string wrappedString ;
}

changed one token in my XAML:

<

TextBox Text="{Binding Path=WrappedString, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>

bound my DirectionList.ItemsSource to an ObservableCollection<StringWrapperForBinding> instance and it worked!

Although I understand the technical reason why it was not working, I'm arguing that binding to a collection of strings is such a common scenario that a developer should not have to wrap the string to make this works. What do you think?