I'm working all day on this, but I'm stuck and don't have a solution. It seems pretty simple..
I have a datagrid with a dynamic amount of columns. Each column has a DataGridTemplateColumn with a CellTemplate and a CellEditingTemplate. The CellEditingTemplate contains a Combobox with values which after the user has selected a value, the TextBox in the CellTemplate has to be updated to the selected value. Normally you would bind this to a known property in an object, but since I don't know how many columns the user needs in advance, I have to create these properties (or something else) at runtime and try to bind this textbox and combobox to this property. How do I solve this? I prefer to not use ExpandoObjects and I'm bounded to .net 4.0
My solution (which didn't work yet) is to add a wrapperobject to a list/array in each row object and then generate a new DataGridTemplateColumn in code where the combobox and the textbox have databindings to this wrapperobject in the array. In XAML this would look something like , but I can't get this 0 changed into 1,2,3,... I could do this in the code behind, but I don't know how to bind it to a WrapperObjectList[X].Value?
Here is a simplified example, hopefully helping understanding what I'd like to achieve:
I have datagrid which is binded to an observablecollection with RowObjects [rowobject represents 1 row.. ;) ]. The first column is fixed, so it's binded to a property in a RowObject.
Now I have to add a dynamic amount of columns after this. This is based on how many items the user has selected (not in the example, but you'll get the point). I replaced this action with clicking the button.
For each column I create a wrapperobject and add this to another observablecollection within the RowObject. This gives me a bit of flexibility since I don't know in advance how many columns will be added. This Wrapperobject contains the value I need to display.
Furthermore, I need this WrapperObject to be binded to a selection in my combobox. So if I edit a row, I select a value from the combobox and then this selected value has to be displayed (set) in the TextBox of the celltemplate.
Example code:
Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
LoadData();
this.DataContext = RowList;
}
private void LoadData()
{
RowList = new ObservableCollection<RowObject>();
WrapperObject lWrapperObject1 = new WrapperObject();
lWrapperObject1.Value = 1;
WrapperObject lWrapperObject2 = new WrapperObject();
lWrapperObject2.Value = 2;
WrapperObject lWrapperObject3 = new WrapperObject();
lWrapperObject3.Value = 3;
WrapperObject lWrapperObject4 = new WrapperObject();
lWrapperObject4.Value = 4;
WrapperObject lWrapperObject6 = new WrapperObject();
lWrapperObject6.Value = 6;
WrapperObject lWrapperObject8 = new WrapperObject();
lWrapperObject6.Value = 8;
RowObject lRowObject1 = new RowObject();
lRowObject1.RowObjectName = "RowObject1";
lRowObject1.WrapperCollection.Add(lWrapperObject1);
lRowObject1.WrapperCollection.Add(lWrapperObject2);
lRowObject1.WrapperCollection.Add(lWrapperObject6);
RowObject lRowObject2 = new RowObject();
lRowObject2.RowObjectName = "RowObject2";
lRowObject2.WrapperCollection.Add(lWrapperObject3);
lRowObject2.WrapperCollection.Add(lWrapperObject4);
lRowObject2.WrapperCollection.Add(lWrapperObject8);
RowList.Add(lRowObject1);
RowList.Add(lRowObject2);
}
public ObservableCollection<RowObject> RowList { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
DataGridTemplateColumn lTemplateColumn = new DataGridTemplateColumn();
lTemplateColumn.Header = "NewDynamicColumn";
var lMyCellTemplate= (DataTemplate)Resources["myCellTemplate"];
var lMyCellEditingTemplate = (DataTemplate)Resources["myCellEditingTemplate"];
// Text="{Binding WrapperCollection[1].Value}" ????
lTemplateColumn.CellTemplate =lMyCellTemplate;
lTemplateColumn.CellEditingTemplate = lMyCellEditingTemplate;
this.MyGrid.Columns.Add(lTemplateColumn);
}
}
public class RowObject : INotifyPropertyChanged
{
public RowObject()
{
WrapperCollection = new ObservableCollection<WrapperObject>();
ComboBoxList = new List<string>();
ComboBoxList.Add("10");
ComboBoxList.Add("20");
}
public ObservableCollection<WrapperObject> WrapperCollection { get; set; }
private string fRowObjectName;
public string RowObjectName
{
get { return fRowObjectName; }
set
{
fRowObjectName = value;
RaisePropertyChanged("RowObjectName");
}
}
public List<string> ComboBoxList { get; set; }
#region Notify property changed
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public class WrapperObject : INotifyPropertyChanged
{
private int fValue;
public int Value
{
get {return fValue;}
set
{
fValue = value;
RaisePropertyChanged("Value");
}
}
#region Notify property changed
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Xaml:
<Window.Resources>
<DataTemplate x:Key="myCellTemplate">
<TextBlock Text="{Binding WrapperCollection[1].Value}" />
</DataTemplate>
<DataTemplate x:Key="myCellEditingTemplate">
<ComboBox ItemsSource="{Binding ComboBoxList}" />
</DataTemplate>
</Window.Resources>
<StackPanel>
<DataGrid x:Name="MyGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding}"
>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding RowObjectName}"/>
<DataGridTextColumn Header="ExampleColumn1" Binding="{Binding WrapperCollection[0].Value}"/>
<DataGridTemplateColumn Header="ExampleColumn2" CellTemplate="{StaticResource myCellTemplate}" CellEditingTemplate="{StaticResource myCellEditingTemplate}"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="Click" Click="Button_Click" Width="100" Margin="20"/>
</StackPanel>
ExampleColumn1 and ExampleColumn2 are the only editable columns in this example and once you click the button, the added column(s) are also editable. Right now I think the solution is something like creating a new binding to the WrapperCollection[2].Value, WrapperCollection[3].Value etc. in the code behind, but how?