I've run into a data binding issue that I cannot seem to resolve. (Warning: full code sample below)
I am developing a grouping control that is able to group different items into named groups. I have a data model that defines this setup that have an observablecollection of groups and each group have an observablecollection of items:
public class GroupingModel
{
public ObservableCollection<Group> Groups { get; set; }
public GroupingModel()
{
Groups = new ObservableCollection<Group>();
}
public void RegroupItem(Item item)
{
var containingGroup = Groups.FirstOrDefault(x => x.Items.Contains(item));
if (containingGroup == null ||
item.GroupName == containingGroup.GroupName)
return;
containingGroup.Items.Remove(item);
var newGroup = Groups.FirstOrDefault(x => x.GroupName == item.GroupName);
if (newGroup == null)
{
newGroup = new Group()
{
GroupName = item.GroupName
};
newGroup.Items.Add(item);
Groups.Add(newGroup);
}
else
{
newGroup.Items.Add(item);
}
}
}
public class Group
{
public ObservableCollection<Item> Items { get; set; }
public string GroupName { get; set; }
public Group()
{
Items = new ObservableCollection<Item>();
}
}
public class Item
{
public string GroupName { get; set; }
public string Title { get; set; }
public object Content { get; set; }
}
This datamodel is populated and bound to the DataContext:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
SetupModel();
}
private GroupingModel m_model = new GroupingModel();
private void SetupModel()
{
var group = new Group()
{
GroupName = "Default",
};
for (int i = 0; i < 5; i++)
{
Item item = new Item()
{
Title = "Item " + i,
GroupName = "Default",
Content = new TextBlock
{
Text = "Hello: " + i,
}
};
group.Items.Add(item);
}
m_model.Groups.Add(group);
DataContext = m_model;
}
private void movegroup(object sender, RoutedEventArgs e)
{
var firstGroup = m_model.Groups.First();
var item = firstGroup.Items.FirstOrDefault();
if (item != null)
{
item.GroupName = "NewGroup";
m_model.RegroupItem(item);
}
}
}
Finally I am binding the whole thing in XAML as shown below:
<UserControl x:Class="ObservableCollectionBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit" >
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button Content="Move first to new group" Click="movegroup" />
<ItemsControl ItemsSource="{Binding Groups}" Grid.Row="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock FontWeight="ExtraBlack" Text="{Binding GroupName}" Margin="0,2,0,2" />
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<toolkit:Expander Header="{Binding Title}" Content="{Binding Title}">
<!--Content="{Binding Content}"-->
</toolkit:Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
When pressing the button I move the first item from the default group into a new group by removing from the observable item collection, creating a new group, inserting the item in this group and adding the new group to the observable collection of groups. This work correctly when the Content property in the Expander is bound to the Title property. You can expand and close items and move them all to the new group.
However, if I change this to bind to the Content property (the line commented out above, which binds to the TextBlock that is created in codebehind) the binding works fine to start with (expanders can be opened and closed, TextBlock is shown) but if you try to move an item that has been expanded, the application dies with an ArgumentException and a message 'Value does not fall within the expected range'.
Am I trying to do something that is not supported (binding the content to a usercontrol created in codebehind), am I setting binding up incorrectly or is something else wrong.
NOTE: I am really hoping to get the binding to work with a usercontrol created in codebehind, since creating a full databinding model for our specific usercontrol would be a quite large undertaking.