I'm stuck on the following binding problem. I have the following class declarations in MainWindow.xaml.cs:
public List<MyArrayClass> MyArrayData { get; set; }
public class MyArrayClass
{
public ObservableCollection<int> MyArrayInt { get; set; }
public ObservableCollection<Thickness> MyArrayBorder { get; set; }
}
And I create the object as follows:
MyArrayClass sample3 = new MyArrayClass
{
MyArrayInt = new ObservableCollection<int> { 0, 1, 2, 3 },
MyArrayBorder = new ObservableCollection<Thickness> { new Thickness(0,0,0,2), new Thickness(0, 0, 0, 2), new Thickness(0, 0, 0, 2), new Thickness(0, 0, 0, 2) }
};
MyArrayData = new List<MyArrayClass>();
MyArrayData.Add(sample3);
Having said that, my objective is to display the contents of MyArrayInt into a set of TextBlocks using the ItemsControl object (I'm trying to understand how it works). In particular, I want to be able to define the border of each TextBlock via binding. My XAML looks like:
<ItemsControl Name="List" ItemsSource="{Binding MyArrayData}" Grid.Row="1" Grid.Column="1">
<!-- List<> -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl Name="MyArrayInt" ItemsSource="{Binding MyArrayInt}">
<!-- Array of int-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Red" BorderThickness="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}, AncestorLevel=2}, Path=MyArrayBorder}">
<TextBlock Text="{Binding Path=.}" Background="Azure" Margin="2,0,0,0"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The result is that MyArrayInt is displayed as I expect, but I get the following error on the BorderThickness binding:
System.Windows.Data Error: 40 : BindingExpression path error: 'MyArrayBorder' property not found on 'object' ''ItemsControl' (Name='List')'. BindingExpression:Path=MyArrayBorder; DataItem='ItemsControl' (Name='List'); target element is 'Border' (Name=''); target property is 'BorderThickness' (type 'Thickness')
The way I read it, it seems that the RelativeSource binding is correct: the Name of the ItemsControl object is 'List', and it's the one binding to MyArrayData. So why doesn't it find MyArrayBorder?
Any suggestion or advice is welcome!
Instead of having two collection properties in MyArrayClass
, you should create a custom type and merge them into one:
public class MyArrayClass
{
public ObservableCollection<MyType> MyArray { get; set; }
}
...
public class MyType
{
public int MyArrayInt { get; set; }
public Thickness MyArrayBorder { get; set; }
}
You could then easily bind to each of the properties in the new type:
<ItemsControl Name="MyArrayInt" ItemsSource="{Binding MyArray}">
<!-- Array of int-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Red" BorderThickness="{Binding MyArrayBorder}">
<TextBlock Text="{Binding MyArrayInt}" Background="Azure" Margin="2,0,0,0"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Given your current setup, you need to use a converter to be able to get a specific item from the MyArrayBorder
collection of the MyArrayClass
.
You can indeed easily bind to the MyArrayBorder
property itself:
BorderThickness="{Binding Path=MyArrayBorder[0],
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}"
...but you cannot bind to an item at a dynamic index (to replace 0
with in the above snipper) in it using pure XAML.