Search code examples
wpfdata-bindingxaml

Binding in WPF to element of array specified by property


Say I've got some TextBlocks on my UI, something like so:

<StackPanel Orientation="Vertical">
    <TextBlock Text="{Binding DessertIndex}" />
    <TextBlock Text="{Binding Food[2]}" />
    <TextBlock Text="{Binding Food[{Binding DessertIndex}]}" />
</StackPanel>

and in my code behind I've got something like this:

public partial class MainWindow : Window
{
    public int DessertIndex
    {
        get { return 2; }
    }

    public object[] Food
    {
        get
        {
            return new object[]{"liver", "spam", "cake", "garlic" };
        }
    }

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }
}

The first two TextBlocks display fine for me, displaying 2 and 'cake' respectively. The third one doesn't accomplish what I'd like, namely use the DessertIndex property to index into that array and also display 'cake'. I did a little searching here on SO for a similar question but didn't find one. Ultimately, I don't want to specify values like 2 in my .xaml file and would like to rely upon a property instead for indexing into that array. Is this possible? If so, what am I doing wrong here?


EDIT:

So what I more closely have is a situation where the data is a List of these object[] and I'm using the above StackPanel as part of a DataTemplate for a ListBox. So the idea, as Mark Heath suggests below, of using a property that dereferences the array doesn't seem to work as I'd want. Ideas?


Solution

  • Another alternative is to use MultiBinding with a converter:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
        <StackPanel Orientation="Vertical">
            <StackPanel.Resources>
                <local:FoodIndexConverter x:Key="foodIndexConverter" />
            </StackPanel.Resources>
            <TextBlock Text="{Binding DessertIndex}" />
            <TextBlock Text="{Binding Food[2]}" />
            <TextBlock>
                <TextBlock.Text>
                    <MultiBinding Converter="{StaticResource foodIndexConverter}">
                        <Binding Path="DessertIndex" />
                        <Binding Path="Food"/>
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </StackPanel>
    </Window>
    

    Then in the code-behind, the converter is defined something like this:

    namespace WpfApplication1
    {
        public class FoodIndexConverter : IMultiValueConverter
        {
            public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                if (values == null || values.Length != 2)
                    return null;
    
                int? idx = values[0] as int?;
                object[] food = values[1] as object[];
    
                if (!idx.HasValue || food == null)
                    return null;
    
                return food[idx.Value];
            }
    
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    }