Dante
Dante

Reputation: 3316

Create a custom DataGrid's ItemsSource

I am working with DataGrids but I am struggling to binding my data since the number of columns varies depending of the info that has to be showed.

So, what I have tried to do is to create and object which contains all the columns and rows that I need at some point and binding this object to the ItemsSource property. Since I have worked with DataGridViews in WindowsForms I have in mind something like this:

DataTable myTable = new DataTable();

DataColumn col01 = new DataColumn("col 01");
myTable.Columns.Add(col01);
DataColumn col02 = new DataColumn("col 02");
myTable.Columns.Add(col02);

DataRow row = myTable.NewRow();
row[0] = "data01";
row[1] = "data02";
myTable.Rows.Add(row);

row = myTable.NewRow();
row[0] = "data01";
row[1] = "data02";
myTable.Rows.Add(row);

But I haven't been able to find a way to do the same thing in WPF since I need some columns to be DataGridComboBoxColumns for example.

Actually I have read many post about it in this site, but none of them helped to me. I am really lost.

Could anyone help me? I just need to be able to create a table which may contain DataGridTextColumns or `DataGridComboBoxColumns, etc, In order to bind this final object to the DataGrid's ItemsSource property.

Hope someone can help me.

Upvotes: 2

Views: 32185

Answers (3)

Kirk H
Kirk H

Reputation: 19

I am new to WPF and used Damascus's example to learn binding of a List to a datagrid. But when I used his answer I found that my datagrid would populate with the correct number of rows but not with any of the properties from the MyObject class. I did a bit more searching then stumbled across what I had to do by accident.

I had to encapsulate the MyObject class properties to have them show. It wasn't enough to have them be public.

Before:

public class MyObject
{
   public int MyID;
   public string MyString;
   public ICommand MyCommand;
}

After:

public class MyObject
{
    private int _myID;

    public int MyID
    {
        get { return _myID; }
        set { _myID = value; }
    }
    private string _myString;

    public string MyString
    {
        get { return _myString; }
        set { _myString = value; }
    }
    private ICommand _myCommand;

    public ICommand MyCommand
    {
        get { return _myCommand; }
        set { _myCommand = value; }
    }
}

Thank you Damascus for a great example and thank you Dante for a great question. I don't know if this is due to a change in version since your post but hopefully this will help others new to WPF like me.

Upvotes: 0

Damascus
Damascus

Reputation: 6641

Okay, let me try to take an example which is similar to your needs

Let's assume we use this class:

public class MyObject
{
   public int MyID;
   public string MyString;
   public ICommand MyCommand;
}

And we are willing to display a DataGrid listing the ID, and having as a second column a Button, with the property MyString as content, which, when clicked, launches the ICommand MyCommand which opens in a new window whatever you want.

Here is what you should have on the View side:

    <DataGrid ItemsSource="{Binding MyList}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="ID" Binding="{Binding MyID}" />
            <DataGridTemplateColumn Header="Buttons">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button Content="{Binding MyString}" Command="{Binding MyCommand}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

This will show a DataGrid taking all the content in an IEnumerable<MyObject> named 'MyList', and shows two columns as defined before.

Now if you need to define the command. First, I recommend you read this introductory link to MVVM and take the RelayCommand class (that's what we're gonna use for your problem)

So, in your ViewModel, the one which defines the MyList, here is how you should define some of the useful objects:

public ObservableCollection<MyObject> MyList { get; set; }

// blah blah blah

public void InitializeMyList()
{
  MyList = new ObservableCollection<MyObject>();
  for (int i = 0; i < 5; i++)
  {
    MyList.Add(InitializeMyObject(i));
  }
}

public MyObject InitializeMyObject(int i)
{
  MyObject theObject = new MyObject();
  theObject.MyID = i;
  theObject.MyString = "The object " + i;
  theObject.MyCommand = new RelayCommand(param =< this.ShowWindow(i));
  return theObject
}

private void ShowWindow(int i)
{
  // Just as an exammple, here I just show a MessageBox
  MessageBox.Show("You clicked on object " + i + "!!!");
}

Upvotes: 6

Kevin DiTraglia
Kevin DiTraglia

Reputation: 26058

A simple example of binding to a ObservableCollection of a custom object. Add more properties to the custom object to match what you want your rows to look like.

 using System.Collections.ObjectModel;

 public MyClass
 {
      public ObservableCollection<MyObject> myList { get; set; }

      public MyClass()
      {
           this.DataContext = this;
           myList = new ObservableCollection<MyObject>();
           myList.Add(new MyObject() { MyProperty = "foo", MyBool = false };
           myList.Add(new MyObject() { MyProperty = "bar", MyBool = true };
      }
 }

 public MyObject
 {
      public string MyProperty { get; set; }
      // I believe will result in checkbox in the grid
      public bool MyBool { get; set; }
      //...as many properties as you want
 }

with xaml

  <DataGrid ItemsSource= "{Binding myList}" />

Might be some small syntax errors, I wrote that entirely within the SO window.

Upvotes: 2

Related Questions