Reputation: 6707
I have trouble to formulate the XAML string to link to a specific element in a multidimensional array.
The DataContext contains the following lines:
private String[] _OneDimension = { "[0]", "[1]" };
private String[][] _Jagged = { new String[] { "[0,0]", "[0,1]" }, new String[] { "[1,0]", "[1,1]" } };
private String[,] _TwoDimension = { { "[0,0]", "[0,1]" }, { "[1,0]", "[1,1]" } };
public String[] OneDimension { get { return _OneDimension; } }
public String[][] Jagged { get { return _Jagged; } }
public String[,] TwoDimension { get { return _TwoDimension; } }
The XAML contains the following lines:
<StackPanel>
<Button Content="{Binding OneDimension[1]}" Width="100" Height="50" />
<Button Content="{Binding Jagged[1][1]}" Width="100" Height="50" />
<Button Content="{Binding TwoDimension[1][1]}" Width="100" Height="50" />
</StackPanel>
The binding to OneDimension
and Jagged
work as expected. The binding to TwoDimension
does not work and appears to be wrong, however the XAML does not allow me to use the separator ,
so I do not know how to bind to a two dimensional array.
This:
<Button Content="{Binding TwoDimension[1,1]}" Width="100" Height="50" />
does not compile because the XAML gets interpreted as having two arguments for the Binding Constructor. Is there some way to escape the parser or is there another way of writing this that I am not aware of?
EDIT:
I just found out that it is possible to escape the separator like this
<Button Content="{Binding TwoDimension[1\,1]}" Width="100" Height="50" />
or just surround the argument with markers like this
<Button Content="{Binding 'TwoDimension[1,1]'}" Width="100" Height="50" />
However this line now leads to an exception: System.ArgumentException
{"Das Array war kein eindimensionales Array."} unfortunatelly C# installed itself in my native language - annoying as shit... so this roughly translates to {"The Array was not a onedimensionale Array."}
Is it actually impossible to bind multidimensional arrays?
Upvotes: 10
Views: 12572
Reputation: 9944
You can bind to a Two dimensional array using a MultivalueConverter defined like this :
class MultiDimensionalCoverter:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return (values[0] as String[,])[(int) values[1], (int) values[2]];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
that MultiDimensionalCoverter get 3 parameters, the Two Dimention array plus the two indexes, and the Xaml will be like this :
<Window.Resources>
<wpfApp:MultiDimensionalCoverter x:Key="MultiDimensionalCoverter"/>
</Window.Resources>
<Grid>
<StackPanel>
<Button Content="{Binding OneDimension[1]}" Width="100" Height="50" />
<Button Content="{Binding Jagged[1][1]}" Width="100" Height="50" />
<Button Width="100" Height="50" >
<Button.Resources>
<system:Int32 x:Key="1">1</system:Int32>
</Button.Resources>
<Button.Content>
<MultiBinding Converter="{StaticResource MultiDimensionalCoverter}">
<Binding Path="TwoDimension"/>
<Binding Source="{StaticResource 1}"/>
<Binding Source="{StaticResource 1}"/>
</MultiBinding>
</Button.Content>
</Button>
</StackPanel>
</Grid>
defining the indexes as properties in your VM is probably more appropriate, i am using fixed value only to demonstrate.
Upvotes: 9
Reputation: 2161
By default WPF XAML does not allow binding to a 2D array like this. Only 1D arrays. However, nothing is impossible. Just time consuming. You will have to create a custom class in order to do this and use that as a way of binding.
/// <summary>
/// This class is a bindable encapsulation of a 2D array.
/// </summary>
/// <typeparam name="T"></typeparam>
public class BindableTwoDArray<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void Notify(string property)
{
var pc = PropertyChanged;
if (pc != null)
pc(this, new PropertyChangedEventArgs(property));
}
T[,] data;
public T this[int c1, int c2]
{
get { return data[c1, c2]; }
set
{
data[c1, c2] = value;
Notify(Binding.IndexerName);
}
}
public string GetStringIndex(int c1, int c2)
{
return c1.ToString() + "-" + c2.ToString();
}
private void SplitIndex(string index, out int c1, out int c2)
{
var parts = index.Split('-');
if (parts.Length != 2)
throw new ArgumentException("The provided index is not valid");
c1 = int.Parse(parts[0]);
c2 = int.Parse(parts[1]);
}
public T this[string index]
{
get
{
int c1, c2;
SplitIndex(index, out c1, out c2);
return data[c1, c2];
}
set
{
int c1, c2;
SplitIndex(index, out c1, out c2);
data[c1, c2] = value;
Notify(Binding.IndexerName);
}
}
public BindableTwoDArray(int size1, int size2)
{
data = new T[size1, size2];
}
public static implicit operator T[,](BindableTwoDArray<T> a)
{
return a.data;
}
}
Then you can bind to XAML:
<TextBlock Text="{Binding MyBindableTwoDArray[2-5]}"/>
Source of solution.
This may affect performance which leads me to question using a multidimensional array to begin with? You may use lists which may be an easier implementation. Take a look at this solution.
Upvotes: 2