Background
I need to allow users to edit an IList<Move> moves
where Move
is an immutable class. Users choose moves from a drop-down (ComboBox
). For example:
[[ for every move slot ]]
<ComboBox DisplayMemberPath="Name" ItemsSource="{DynamicResource MoveCollection}" SelectedValue="[[ move of the move slot ]]" />
[[ end for ]]
My approach
I am using a ListView
to iterate over the collection, and a DataTemplate
to create the ComboBox
instances, as follows:
<ListView ItemsSource="{Binding Moves}">
<ListView.ItemTemplate>
<DataTemplate>
<ComboBox DisplayMemberPath="Name" ItemsSource="{DynamicResource MoveCollection}" SelectedValue="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I get an exception with this ("Two-way binding requires Path or XPath."), so I resorted to the following binding expression for SelectedValue
:
{Binding Path=DataContext, RelativeSource={RelativeSource Self}}
This makes it compile and run. However, the moves
collection is not updated as the user makes changes. Obviously, only the data context is being changed, not the actual value.
I believe this is an issue because the IList<Move>
is simply treated as a read-only collection by WPF, and changes to the combo box's value do not and cannot modi开发者_如何学运维fy that collection. In code, WPF cannot do the following:
moves[x] = Resources["MoveCollection"][y];
I would like for changes in the combo box's value to update the moves
collection. How can I accomplish this?
Yes, your suspicions about IList are partially correct. Let's take a look at what's actually happening under the hood: ListView is handed a collection of items. For each item, it creates a new copy of the data template within a panel specified by the panel template. The data template is going to be handed a reference to the object it's interested in as its DataContext. So even if you changed the value successfully, the underlying scenario looks like this in C#:
List yourItems = ...; foreach(string yourItem in yourItems) { someControl.DataContext = yourItem;
//you change it here someControl.DataContext = yourNewItem; }
You wouldn't expect the above code to change the list. Although it's a bit different, I think what's going on here is similar.
I believe the simplest way to do what you're trying to do would be to make an IList>. Wrapper would just be a dummy class wrapping a Move. Binding would then change the Move on the Wrapper. On the backend, it'll be trivial to unwrap the list when you need it with a little LINQ.
How's that sound?
精彩评论