How to make TwoWay binding with templates?
odinsacred opened this issue · 10 comments
Hello! Can anybody help me? I wanna bind 2D int array to DataGrid, but I don't understand how to create TwoWay binding. Now I'm use this code:
<DataGrid Grid.Column="1" Grid.Row="1" x:Name="AutoColumns"
dataGrid2D:ItemsSource.Array2D="{Binding Path=Data2D, Mode=TwoWay}"
dataGrid2D:ItemsSource.ColumnHeadersSource="{Binding ColumnHeaders}"
dataGrid2D:ItemsSource.RowHeadersSource="{Binding RowHeaders}"
ColumnWidth="50"
SelectionUnit="Cell"
IsReadOnly="False"
IsEnabled="True">
<dataGrid2D:Cell.Template>
<DataTemplate>
<TextBlock Text="{Binding Path=., StringFormat=X4}" IsEnabled="true"/>
</DataTemplate>
</dataGrid2D:Cell.Template>
<dataGrid2D:Cell.EditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=., StringFormat=X4}" />
</DataTemplate>
</dataGrid2D:Cell.EditingTemplate>
</DataGrid>
Also I'm tried to add an attributes like "Mode=TwoWay, BindsDirectlyToSource=True, UpdateSourceTrigger=LostFocus, NotifyOnSourceUpdated=True" In data templates, but it's do nothing effect. Now it's allow me to entering some numbers in cells, but not save to my array. And one detail - everything work good without templates, but I need them.
Is the type you are binding to int[,]
? If so you need to wrap the elements and bind to a property. This is the same limitation as if you do:
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Values}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Value">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding .}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
with:
public class ViewModel
{
public ObservableCollection<int> Values { get; } = new ObservableCollection<int> {1, 2, 3};
}
Thanks for answer! Yes, it's about int[,]
. I'm novice in WPF, so I'm not completely understood this
public ObservableCollection<int> Values { get; } = new ObservableCollection<int> {1, 2, 3};
This is an 1D array, right? So for 2D array I'm need
public ObservableCollection<int> Values { get; } = new ObservableCollection<int> {{1, 2, 3}, {1,2,3}};
Isn't it?
What I want: view and edit values in HEX format, in addition I need to set names of rows and columns. Today I'm readed issue "Scientific Notation for Numeric Data", created by kaneorotar, he has a similar problem. Maybe we will try to write a complete example with templates and twoWay binding for other people, who will have this problem in the future?
Now I'm try do this: (but it's not working)
C# code:
public ObservableCollection<int[,]> Values
{
get => this.values;
set
{
if (Equals(value, this.values))
{
return;
}
this.values = value;
NotifyOfPropertyChange(() => Values);
}
}
// Constructor:
ObservableCollection<int[,]> a = new ObservableCollection<int[,]>();
a.Add(new int[,] { { 1, 2, 3 }, { 1, 2, 3 } });
Values = a;
XAML code:
<DataGrid Grid.Column="1" Grid.Row="1" x:Name="AutoColumns"
dataGrid2D:ItemsSource.Array2D="{Binding Path=Values}"
dataGrid2D:ItemsSource.ColumnHeadersSource="{Binding ColumnHeaders}"
dataGrid2D:ItemsSource.RowHeadersSource="{Binding RowHeaders}"
ColumnWidth="50"
SelectionUnit="Cell"
IsReadOnly="False"
IsEnabled="True">
<dataGrid2D:Cell.Template>
<DataTemplate>
<TextBlock Text="{Binding Path=., StringFormat=X4}"/>
</DataTemplate>
</dataGrid2D:Cell.Template>
<dataGrid2D:Cell.EditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=., StringFormat=X4}" />
</DataTemplate>
</dataGrid2D:Cell.EditingTemplate>
</DataGrid>
The code was not a suggestion for a fix, it was an example of the limitation you hit.
And so, should I hope for comlete example, or I must create it by myself?
I'll ping you if I write one but not sure when I will find the time.
Ok, anyway, thanks for support))
@odinsacred for now you can use converter like this:
public class EditableInt2DConverter : IValueConverter
{
class ItemWrapper
{
int[,] container;
int i, j;
public ItemWrapper(int[,] container, int i, int j)
{
this.container = container;
this.i = i;
this.j = j;
}
public int Value
{
get => container[i, j];
set => container[i, j] = value;
}
}
public object Convert(object value, Type targetType, object p, CultureInfo ci)
{
var array2d = (int[,])value;
var n = array2d.GetLength(0);
ItemWrapper[,] items = new ItemWrapper[n, n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
items[i,j] = new ItemWrapper(array2d, i, j);
return items;
}
public object ConvertBack(object value, Type targetType, object p, CultureInfo ci) =>
throw new NotSupportedException();
}
dataGrid2D:ItemsSource.Array2D="{Binding Path=Data2D, Converter={StaticResource EditableInt2DConverter}}"
so, you will bind to Value
in templates.
<dataGrid2D:Cell.Template>
<DataTemplate>
<TextBlock IsEnabled="true" Text="{Binding Path=Value, StringFormat=X4}" />
</DataTemplate>
</dataGrid2D:Cell.Template>
<dataGrid2D:Cell.EditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Value, StringFormat=X4}" />
</DataTemplate>
</dataGrid2D:Cell.EditingTemplate>
@FoggyFinder Thank you very much!