Exception thrown when binding ItemsControl to a UserControls collection
In my application a templated form needs to show a list of usercontrols in a stackpanel. I figured I could use ItemsControl on the form and bind it to a collection that exposes the "child usercontrols". This approach works fine the first time when the templated form loads but if I make any changes to the Observable collection of child usercontrols, I get an exception "Value does not fall within the expected range".
I have looked thru many posts but none of the solutions seem to have worked. I am pasting a sample code that shows the issue I am talking about. The sample code has
Mainpage with ItemsCOntrol in it. The Itemscontrol is bound to an Observablecollection of type AnyControl.
Class AnyControl has a public property AttachedControl. The AttachedControl is type object.
When the Mainpage loads, I create 2 instances of AnyControl and set the AttachedControl property to instances of Textboxes.
Mainpage has 3 buttons
- Show All. which sets the datacontext of the mainpage and the two textboxes show up on the form.
- Remove Second Control. clicking this button removes second entry from the ObservableCollection and the second textbox disappears.
- Restore Second Control. clicking this button adds the second entry back into the Observable Collection. Logically the second textbox should show up on the form but instead I get the exception.
The code starts here..... MainPage.xaml.cs
public partial class MainPage : UserControl, INotifyPropertyChanged
{
public AnyControl control1;
public AnyControl control2;
public MainPage()
{
InitializeComponent();
control1 = new AnyControl(new TextBox() { Text = "First Textbox" });
control2 = new AnyControl(new TextBox() { Text = "Second Textbox" });
_allVMs = new ObservableCollection<AnyControl>();
_allVMs.Add(control1);
_allVMs.Add(control2);
}
private ObservableCollection<AnyControl> _allVMs;
public ObservableCollection<AnyControl> ActiveViews
{
get { return _allVMs;}
set
{
_allVMs = value;
NotifyPropertyChanged("ActiveViews");
}
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
this.LayoutRoot.DataContext = this; // RecordTemplate;
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
this.ActiveViews.RemoveAt(1);
NotifyPropertyChanged("ActiveViews");
}
private void Button3_Click(object sender, RoutedEventArgs e)
{
this.ActiveViews.Add(control2);
NotifyPropertyChanged("ActiveViews");
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
AnyControl.cs
public class AnyControl
{
public object AttachedControl { get; set; }
public AnyControl(object control)
{
AttachedControl = control;
}
}
MainPage.xaml
<UserControl x:Class="SilverlightApplication2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:layoutToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"
xmlns:layoutPrimitivesToolkit="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Layout.Toolkit"
xmlns:local="clr-namespace:SilverlightApplication2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<ItemsControl ItemsSource="{Binding ActiveViews}" x:Name="ItemsControl">
<ItemsControl.ItemsPanel >
<ItemsPanelTemplate x:Name="a7777">
<VirtualizingStackPanel Orientation="Vertical" x:Name="222"/>
</ItemsPanelTemplate>
</ItemsContr开发者_运维知识库ol.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding AttachedControl}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Orientation="Horizontal" Grid.Row="1">
<Button Content="Show All" Height="22" Click="Button1_Click" Margin="5,0,5,0" />
<Button Content="Remove second control" Height="22" Click="Button2_Click" Margin="5,0,5,0"/>
<Button Content="Restore second control" Height="22" Click="Button3_Click" Margin="5,0,5,0"/>
</StackPanel>
</Grid>
Any Help is much appreciated. Thanks A
Wrote a custom control to solve this issue. Since all I wanted was to show usercontrols within a "parent" form, I ended up creating a custom panel control. Added a dependency property called ItemsSource to it. The handler for this property adds the custom controls in its collection to the panel's CHildren collection.
@ColinE, I did make sure that before adding a child control to the panel I check if it is child of another panel. If it is I obtain a reference to its parent panel control and remove the control from that parent panel's children collection.
It seems to work just fine.
Thanks for the comments. A
精彩评论