WiiMaxx
WiiMaxx

Reputation: 5420

how to add List<List<MyClass>>() as DataGrid.ItemsSource

The Problem

if i have such a List let's call it Rows and i wan't to add like myDataGrid.ItemsSource = Rows; than i get in all Columns the first myClass per subList

it looks like

 Column0  |  Column1  |  Column2  |  Column3

firstrow0 | firstrow0 | firstrow0 | firstrow0
firstrow1 | firstrow1 | firstrow1 | firstrow1
firstrow2 | firstrow2 | firstrow2 | firstrow2

The Code

XAML

    <DataGrid Name="myDataGrid" AutoGenerateColumns="False">
        <DataGrid.Columns >
            <DataGridTemplateColumn>                    
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate  DataType="{x:Type vmv:myClass}">
                        <TextBlock Text="{Binding Name}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate DataType="{x:Type vmv:myClass}">
                        <TextBlock Text="{Binding Name}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>                  
            </DataGridTemplateColumn>

            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate  DataType="{x:Type vmv:myClass}">
                        <TextBlock Text="{Binding Name}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

        </DataGrid.Columns>
    </DataGrid>

behind

        var list = new List<List<myClass>>();

        for (int row = 0; row < 3; row++)
        {
            var myRow = new List<myClass>();
            for (int col = 0; col < 5; col++)
                myRow.Add(new myClass() { ID = col, Name = "Row"+row +" Column:" + col });
            list.Add(myRow);
        }

        myDataGrid.ItemsSource = list.AsEnumerable<IEnumerable>();

myClass

public class myClass
{
    public int ID { get; set; }
    public string Name { get; set; }
    // other stuff
}

The Question

What do i need to get this working. Do i need to cast it in some way? Do i need some other Object as a List<>? Anything that could help is greatly appreciated!

EDIT

in RL Code i will not be able to change the DataTemplate part because it part of XAMLFile that will created by my company so it will fit so parameters but original it will only be for printing. I only load it to Find("ItemTemplate") => cast it as DataTemplate and us it to provide a WYSIWYG for the DataGridCell because the Width and Height will be different from PrintTemplate to PrintTemplate

Solution

the following code is the solution for my specific Problem take also a look at michele Answer

        #region example Datacreation
        var list = new List<IEnumerable>();

        for (int row = 0; row < 5; row++)
        {
            var myRow = new List<myClass>();
            for (int col = 0; col < 5; col++)
            {
                myRow.Add(new myClass() { ID = col, Name = "Row" + row + " Column:" + col });
            }
            list.Add(myRow);
        }
        #endregion

        #region FileToDataTemplate
        var myXamlFile = "<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' "
                   + "xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' "
                   + "xmlns:vmv='clr-namespace:toDataGrid;assembly=toDataGrid' " //namespace
                   + "SizeToContent='WidthAndHeight'>"
                   + "<Window.Resources>"
                       + "<DataTemplate x:Name='myFileCellTemplate' DataType='{x:Type vmv:myClass}'>"
                            + "<TextBlock Text='{Binding Name}'/>"
                        + "</DataTemplate>"
                   + "</Window.Resources>"
            // some stuff
             + "</Window>";

        Window myWindow = (Window)XamlReader.Load(XmlReader.Create(new StringReader(myXamlFile)));
        myWindow.Close();

        DataTemplate myCellTemplate = (DataTemplate)myWindow.FindName("myFileCellTemplate");
        #endregion

        DataGrid myDataGrid = new DataGrid();

        #region dyn DataGridcreation
        for (int col = 0; col < 5; col++)
        {
            #region HelperDataTemplatecreation
            var myResourceDictionaryString = "<ResourceDictionary xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' "
                                                                   + "xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' "
                                                                   + "xmlns:vmv='clr-namespace:toDataGrid;assembly=toDataGrid'>" //namespace

                                                                       + "<DataTemplate DataType='{x:Type vmv:myClass}'>"
                                                                           + "<Label Content='{Binding [" + col + "]}'/>"
                                                                       + "</DataTemplate>"
                                                  + "</ResourceDictionary> ";

            ResourceDictionary ResDic = (ResourceDictionary)XamlReader.Load(XmlReader.Create(new StringReader(myResourceDictionaryString)));

            DataTemplate HelpDTemp = (DataTemplate)ResDic[ResDic.Keys.Cast<Object>().First()];
            #endregion

            DataGridTemplateColumn templateColumn = new DataGridTemplateColumn();


            templateColumn.Header = col;

            templateColumn.CellTemplate = HelpDTemp;
            templateColumn.CellEditingTemplate = HelpDTemp;

            myDataGrid.Columns.Add(templateColumn);
        }
        #endregion


        myDataGrid.Resources.Add(new DataTemplateKey(typeof(myClass)), myCellTemplate);
        myDataGrid.ItemsSource = list.AsEnumerable<IEnumerable>();

Upvotes: 1

Views: 796

Answers (1)

michele
michele

Reputation: 2091

In your shoes I will generate all the DataGridColumns programmatically (as suggested in the comments) so you can assign the correct DataContext to every cell and everything will be really dynamic.

But, if your question it's only about a DataBinding problem, your example will work if you change your TextBlock DataBinding expression to:

<TextBlock Text="{Binding [0].Name}"/>

for the first data template, [1].Name and [2].Name for the other two DataTemplates. That's will work beacuse your row DataContext is a List<T>, so adding [#] to your DataBinding expression will set the data context of every cell to the correct object.

EDIT - Based on comments below: How to create datagridcolumn programmatically using a given datattemplate from resources.

In code behind

//In your example you have 5 columns    
for (int c = 0; c < 5; c++)
{
  DataGridTemplateColumn column = new DataGridTemplateColumn();
  //Basically i will wrap your DataTemplate in a ContentPresenter
  //The ContentProperty is set to point to the correct element of your list                  
  var factory = new FrameworkElementFactory(typeof(ContentPresenter));
  factory.SetBinding(ContentPresenter.ContentProperty, new Binding(string.Format("[{0}]", c.ToString())));
  factory.SetValue(ContentPresenter.ContentTemplateProperty, this.FindResource("YourTemplateName") as DataTemplate);
  column.SetValue(DataGridTemplateColumn.CellTemplateProperty, new DataTemplate { VisualTree = factory });
  myDataGrid.Columns.Add(column);
}

Upvotes: 1

Related Questions