views:

1609

answers:

2

I have a ObservableCollection that's bound to a ListBox in WPF. I want the ListBox to be editable, and for the editing changes to be saved to the collection. Since WPF doesnt provide an editable listbox, I've tried creating my own by changing the ListBox.ItemTemplate.

<ListBox.ItemTemplate>
    <DataTemplate>      
     <TextBox Name="EditableText" Text="{TemplateBinding Content}"/>
    </DataTemplate>
</ListBox.ItemTemplate>

Changing the ItemTemplate gives me editable boxes, but any changes to the textboxes dont get saved to the ObservableCollection. Is there a way to have an editable ListBox with two way binding?

A: 

Bind to a model property -- i.e. a property of the data object -- rather than to a view property such as Content. For example:

// model class
public class Widget : INotifyPropertyChanged
{
  public string Description { ... }
}

<!-- view -->
<DataTemplate>
  <TextBox Text="{Binding Description}" />
</DataTemplate>

Note this will not work if your ItemsSource is ObservableCollection (because there's no property to bind to).

itowlson
I don't think I can bind the textbox text to the data object directly. In your example you bind to a single string, but my class has a collection of them, I cant set the Text Property to a list of strings.
Varin
The DataTemplate gets repeated for each member of the Items collection, with the binding set to that member.
itowlson
+2  A: 

You cannot do it this way.

To achieve that kind of trick, you would need your items to be "holder classes" that expose a property you can bind your textbox to.

To understand it, imagine the following pseudo sequence of calls:

class ListBox
{
  Bind(Items)
  {
    foreach(var item in Items)
    {
      DataTemplate Template = LoadTemplateForItem(item.GetType()); // this is where your template get loaded
      Template.Bind(item); //this is where your template gets bound
    }
  }
}

Your template (the DataTemplate with the listbox) is loaded and the item (which I assume is a string in your case) gets passed in. At this point, it only knows the string, and cannot influence anything upwards. A two-way binding cannot influence the collection because the template does not know in which context it is being used, so it cannot reach back to the original collection and modify its contents. For that matter, this is the same thing for the TextBox. If it is not given a conainer and a property name, it has nowhere to "store back" the changes. This basically the same as passing a string into a function call. The function cannot change which string was passed in (ignoring tricks such as by-reference argument passing).

To get back to your case, you need to build a collection of objects which expose a property containing the value that needs to be edited:

public class MyDataItem
{
  property string Data { get; set;}
}

Then you can bind your ListBox to a collection of those items and modifiy your datatemplate:

<ListBox.ItemTemplate>
    <DataTemplate>                                              
            <TextBox Name="EditableText" Text="{Binding Data, Mode=TwoWay}"/>
    </DataTemplate>
</ListBox.ItemTemplate>
Denis Troller
I have a similar problem, but because I'm very new with WPF I don't know how to write {Binding Data, Mode=TwoWay} if I have list of strings and there is no actual property which can be bound
Sergej Andrejev
You cannot, that's the point of my answer...You need to take your list of strings, create a list of objects holding those strings (each object holds one string, accessible through a property), and display that secondary list in the ListBox.
Denis Troller