Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF DataGrid: Binding a Collection Property to Column

I have a Class called Person:

public class Person
{
  private string _Name;
  private ObservableCollection<Smartphone> _Smartphones;

  // Properties
}

public class Smartphone
{
    private string _Manufacturer;
    private bool _IsWorking;

    // Properties
}

And in my View I have a DataGrid. My Question is:

Is there a way to make my DataGrid look like this:

enter image description here

All Persons have the same Smartphones in their Collection, but with different values vor "IsWorking"...

EDIT: I've tried it with a DataGrid in a DataGrid like:

<DataGrid ItemsSource="{Binding PersonCollection}"
                  AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Name}" Header="Name" />
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <DataGrid ItemsSource="{Binding Smartphones}">
                            </DataGrid>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>

But it is not the "solution" I would like to have...

EDIT 2: It would be nice, if there could be a Checkbox instead of "true/false" :) ...

like image 708
Kajetan Jauk Avatar asked Aug 12 '14 14:08

Kajetan Jauk


1 Answers

Try this

SmartPhone.cs Person.cs

public class Person 
{
    public string Name { get; set; }

    public ObservableCollection<SmartPhone> SmartPhones { get; set; }
}

public class SmartPhone
{
    public string Manufacturer { get; set; }

    public bool IsWorking { get; set; }
}

Edit If you want Dynamically try this

Custom DataGrid and Converter

    public class MyDataGrid:DataGrid
{
    public ObservableCollection<string> ColumnHeaders
    {
        get { return GetValue(ColumnHeadersProperty) as ObservableCollection<string>; }
        set { SetValue(ColumnHeadersProperty, value); }
    }

    public static readOnly DependencyProperty ColumnHeadersProperty = DependencyProperty.Register("ColumnHeaders", typeof(ObservableCollection<string>), typeof(MyDataGrid), new PropertyMetadata(new PropertyChangedCallback(OnColumnsChanged)));

    static void OnColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid=d as MyDataGrid;
        dataGrid.Columns.Clear();
        //Add Person Column
        dataGrid.Columns.Add(new DataGridTextColumn() { Header = "Name", Binding = new Binding("Name") });
        //Add Manufactures Columns
        foreach (var value in dataGrid.ColumnHeaders)
        {
            var column=new DataGridCheckBoxColumn(){Header=value,Binding=new Binding("SmartPhones"){ConverterParameter=value,Converter=new ManufacturerConverter()}};
            dataGrid.Columns.Add(column);
        }
    }
}

public class ManufacturerConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var smartPhones = value as IEnumerable<SmartPhone>;
        if (smartPhones != null && parameter!=null)
        {
            var phone=smartPhones.FirstOrDefault(s => s.Manufacturer == parameter.ToString());
            if (phone != null)
                return phone.IsWorking;
            return false;
        }
        return false;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

xaml

    <StackPanel>
    <local:MyDataGrid AutoGenerateColumns="False"
                      ColumnHeaders="{Binding ColumnHeaders}"
                      ItemsSource="{Binding PersonCollection}"
                      CanUserAddRows="False" IsReadOnly="True">

    </local:MyDataGrid>
</StackPanel>

xaml.cs

    public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

ViewModel

public class ViewModel
{
    public ViewModel()
    {
        ColumnHeaders = new ObservableCollection<string>();
        PersonCollection = new ObservableCollection<Person>()
        {
            new Person(){Name="Foo",
                SmartPhones=new ObservableCollection<SmartPhone>()
                {new SmartPhone(){Manufacturer="Manufacturer1",IsWorking=true}
                    ,new SmartPhone(){Manufacturer="Manufacturer2",IsWorking=false}}}
        , new Person(){Name="Bar",
                SmartPhones=new ObservableCollection<SmartPhone>()
                {new SmartPhone(){Manufacturer="Manufacturer1",IsWorking=true}
                    ,new SmartPhone(){Manufacturer="Manufacturer2",IsWorking=false}
                    ,new SmartPhone(){Manufacturer="Manufacturer3",IsWorking=true}}}

        , new Person(){Name="FooBar",
                SmartPhones=new ObservableCollection<SmartPhone>()
                {new SmartPhone(){Manufacturer="Manufacturer1",IsWorking=true}
                    ,new SmartPhone(){Manufacturer="Manufacturer2",IsWorking=false}
                    ,new SmartPhone(){Manufacturer="Manufacturer3",IsWorking=true}
                    ,new SmartPhone(){Manufacturer="Manufacturer4",IsWorking=false}
                    ,new SmartPhone(){Manufacturer="Manufacturer5",IsWorking=true}
                }}


        };
        foreach (var item in PersonCollection.SelectMany(s=>s.SmartPhones).Select(s=>s.Manufacturer).Distinct())
        {
            ColumnHeaders.Add(item);
        }
    }

    public ObservableCollection<Person> PersonCollection { get; set; }

    public ObservableCollection<string> ColumnHeaders { get; set; }
}

Output

Output

I hope this will help.

like image 184
yo chauhan Avatar answered Nov 16 '22 01:11

yo chauhan